Go 构建约束的使用方式
Go 构建约束的使用方式
ivansli在go中进行编译时,可能会带一些指示条件(如:不同平台、架构等)让编译器选择满足条件的代码参与编译,将不满足条件的代码舍弃。这就是条件编译
,也可称为构建(编译)约束
。
目前,支持的构建约束有2种使用方式:
1.文件后缀
2.编译标签(build tag)
两者区别:
- 文件后缀方式多用于交叉编译 (跨平台)。
- 编译标签方式多用于条件编译 (也可用于交叉编译)。
构建约束官方文档
文件后缀的使用方式
编译器根据文件后缀来选择具体文件来参与编译操作,格式如下:
1 | $filenamePrefix_$GOOS.go |
$filenamePrefix
: 源码文件名称前缀(一般为包名称)。$GOOS
: 表示操作系统,从环境变量中获取。$GOARCH
: 表示系统架构,从环境变量中获取。
例如,Go源码中os包的Linux、windows实现
1 | src/runtime/os_linux.go |
使用编译标识编译
使用编译标识指示编译器选择对应的文件进行编译(也称为: 交叉编译),可以得到非当前平台二进制文件。
1 | // 非linux平台编译出linux平台运行的二进制文件 |
不使用编译标识编译
1 | go build |
不使用编译标识,编译器会根据当前环境编译出当前平台二进制文件。
编译标签(build tag)的使用方式
编译标签写法
目前,Go的构建约束支持两种写法:
①.// +build <tags>
②.//go:build <tags>
两种编译标签相同点
1.在源码文件顶部添加 (在所有代码之前),来决定文件是否参与编译
2.与其他注释之间需要存在一个空行
两种编译标签区别
1.起始位置是否包含空格// +build
与双斜线之间包含空格//go:build
与双斜线之间不存在空格
2.Go不同的版本支持
1 | Go versions 1.16 and earlier used a different syntax for build constraints, with a "// +build" prefix. |
在Go的1.16以及之前的版本使用 // +build
前缀来标识构建约束。
gofmt命令在遇到 // +build
前缀来标识构建约束时会添加一下等效的 //go:build
构建约束。
3.同一文件中编译标签的行数
// +build
在一个文件中可以存在多行//go:build
在一个文件中只能存在一行,超过一行则会报错
例如,Go源码 src/math/big/arith_mipsx.s
中
1 | //go:build !math_big_pure_go && (mips || mipsle) |
在该文件中,// +build
有两行,//go:build
仅有一行。
4.多个tag之间的连接符
// +build
多个tag之间,可用的连接符
1 | 空格表示:AND |
//go:build
多个tag之间,可用的连接符
1 | && 表示:AND |
从这里可以看出 //go:build
多个tag之间的连接符更接近于代码规范,也更加容易理解(这也是替代// +build
的一个原因)。
编译标签中多个tag的组合方式
tag 可指定为以下内容:
操作系统,环境变量中GOOS的值
如:linux、darwin、windows等等
操作系统的架构,环境变量中GOARCH的值
如:arch64、x86、i386等等
使用的编译器
如:gc或者gccgo,是否开启CGO,cgo
golang版本号
如:Go Version 1.1为go1.1, Go Version 1.12版本为go1.12,以此类推
其它自定义标签
通过
go build -tags 自定义tag名称
指定tag值
示例
1 | // +build linux,386 darwin,!cgo |
自定义tag的使用方式
新建 buildtag 项目,包含文件如下:
1 | ➜ tree |
main.go 文件
1 | package main |
demo_tag.go 文件
1 | //go:build use |
demo_not_tag.go 文件
1 | //go:build !use |
从上面代码可以看到 demo_tag.go 文件中 //go:build use
与 demo_not_tag.go 文件中 //go:build !use
。
分别使用 go build
与 go build -tags use
执行,结果如下所示:
1 | ➜ go build |
可以看出:
- 使用
go build
调用的demo方法为 demo_not_tag.go 文件中demo方法 - 使用
go build -tags use
调用的demo方法为 demo_tag.go 文件中demo方法
如果有多个tag可以使用空格分隔
例如:go build -tags "use use1 use2"
总结一句话就是:编译器根据 tag标识 有选择性的加载对应文件进行编译
Go源码中关于构建约束的部分追溯
src/cmd/asm/internal/lex/tokenizer.go
1 | func (t *Tokenizer) Next() ScanToken { |
在词法分析时, 存在 "//go:build"
开头的文本,则标识为 token 为 BuildComment
src/cmd/asm/internal/lex/lex.go
1 | // A ScanToken represents an input item. It is a simple wrapping of rune, as |
BuildComment 代表 //go:build or +build
注释
src/cmd/fix/buildtag.go
1 | package main |
Remove +build comments from modules using Go 1.18 or later
明确说明在 1.18或者更新的版本中会移除 +build
。
src/cmd/fix/buildtag_test.go
1 | package main |
buildtagTests
中 In、Out可以看出不同版本对于 // +build
的处理方式。
fix的作用
Fix finds Go programs that use old APIs and rewrites them to use newer ones.
After you update to a new Go release, fix helps make the necessary changes to your programs.