Go条件编译
Overview
最近在开发过程中遇到一个比较在编码上无解的问题,最终通过条件编译得到一个比较满意的方案。对于Go的条件编译,可能很多人都了解,甚至不知道。Go通过在行注释的前面编写如下代码来实现条件编译。
1// +build
条件编译的指令可能出现在任何源代码中,不止是*.go文件,可能是go汇编文件。无论是何种源文件,条件编译指令一定都出现在文件的顶部,并且在空行或者其他行注释之前。所以条件编译指令也必须在package语句之前。
编译规则
可以将 // +build 后面的内容当成一个表达式。当表达式返回true时,当前文件参与编译,反之不参与编译。
多个片段之间的空格表示它们之间是OR的关系。如下,表示GOOS值是linux或者darwin时,本文件参与编译。
1// +build linux darwin
- 多个片段之间的,表示它们之间是AND的关系。如下,表示GOOS值是linux且是amd64架构时,本文件参与编译。
1// +build linux,amd64
- 以!xxx开头的片段表示当tag xxx设置时,当前文件不参与编译。如下,表示GOOS值是linux时,本文件不参与编译。
1// +build !linux
- 单文件包含多个条件编译指令时,它们是AND的关系。如下,表示GOOS值是linux且是amd64架构时,本文件参与编译。
1// +build linux
2// +build amd64
一些内建的关键字。
GOOS
的值,目标操作系统,如linux,darwin。GOARCH
的值,目标架构,如amd64。- 编译器,
gc
或者gccgo
。 cgo
如果cgo支持,编译。gox.x
只在特定go版本进行编译,不支持beta or minor版本号的条件编译。go build
命令的其他tag。
文件名实现条件编译。条件编译支持以下三种格式(
源码文件名去除类型后缀和_test后缀后
):*_GOOS
GOOS值与文件名中的GOOS一致时参与编译。*_GOARCH
GOARCH值与文件名中的GOARCH一致时参与编译。*_GOOS_GOARCH
GOARCH,GOOS值与文件名中的GOARCH,GOOS一致时参与编译。
如
source_windows_amd64.go
该文件只在windows
系统的amd64
架构下进行编译。
举个栗子
示例的文件目录:
1$ tree .
2$ .
3$ ├── etcd.go
4$ ├── go.mod
5$ ├── main.go
6$ └── redis.go
etcd.go
当tags中出现etcd字符时,不参与编译。
1// +build !etcd
2
3package main
4
5fun init() {
6 println("etcd init")
7}
redis.go
当tags中出现redis字符时,不参与编译。
1// +build !redis
2
3package main
4
5fun init() {
6 println("redis redis")
7}
main.go
1package main
2
3func main() {
4 println("hell world!")
5}
下面我们来看看效果吧!
- 直接编译,不执行条件编译
1$ go run .
2$ etcd init
3$ redis init
4$ hell world!
可以看到,etcd.go,redis.go,main.go都被编译了。
- 不编译redis.go文件
1$ go run -tags redis . # 我们使用 `-tags` 来设置编译条件。
2$ etcd init
3$ hell world!
这时候我们看到,只有etcd.go和main.go被编译了,redis.go中的init方法没有被执行。
- 不编译etcd.go文件
1$ go run -tags etcd . # 我们使用 `-tags` 来设置编译条件。
2$ redis init
3$ hell world!
这时候我们看到,只有redis.go和main.go被编译了,main.go中的init方法没有被执行。
- 不编译etcd.go和redis.go文件
1$ go run -tags etcd,redis . # 我们使用 `-tags` 来设置编译条件。
2$ hell world!
这时候我们看到,只有main.go文件中的main函数被执行了,其他文件中的init方法均没有被执行。
总结
在go的源代码中条件编译使用的非常广泛。比如某些功能在不同操作系统的实现不一样,这时候我们就需要针对不同操作系统分别编写代码,但这些代码都在一个目录中,如果没有条件编译将无法编译成功。又或者我们的配置信息可能来自Redis,Etcd,ZooKeeper等不同的配置源,但在运行时我们只用到Etcd,这时候我们可以对代码进行拆分并编写条件编译指令,在编译时只编译Etcd数据源的代码以减小不必要的依赖。
有些靠编写代码没法控制事情,通过条件编译也许可以帮助你,总之掌握条件编译可以帮助我们更好的完成开发工作,甚至实现一些普通程序员无法理解的“黑科技”。
更多信息请查看官方介绍Build Constraints。
comments powered by Disqus