Go 语言的构建约束
Golang 语言作为21世纪出现的少数语言之一,它有吸收并继承了上世纪发明的那些语言的优点。
build constraints
(构建约束)就是其中不错的设计。有了这些设计, 我们可以在源码中通过注释的方式指定编译选项,比如只允许在 Linux 下, 或者在 386 的平台上编译之类的情况; 还可以通过文件名来约束构建, 比如 *_linux.go
, 就是只允许在Linux 下编译, *_windows_amd64.go
就是只允许在 windows 和 amd64 下进行编译。
构建约束可以在很多文件中进行使用, 不单单适用 .go
文件。但是必须要注意的是, 通过注释实施构建约束的话, 必须要放在文件的开头, 要优先于空行和其它注释(包含包注释和协议注释)之前。也就是说必须在 package
语句的前面写。
因为 Go 的 godoc 是可以提取代码中的注释然后转换为文档的, 在 package 语句之前写的注释会被认为是包级别的注释。而构建约束又得在所有注释之前, 那么为了区分包级别的注释, 官方给出的方法是: To distinguish build constraints from package documentation, a series of build constraints must be followed by a blank line.
要在构建约束与包级别的注释之间添加空行进行区分。
通过注释实施的构建约束还可以进行逻辑表达, 即 and, or 之类的语义。Go 官方定义的是:
A build constraint is evaluated as the OR
of space-separated options. Each option evaluates as the AND
of its comma-separated terms. Each term consists of letters, digits, underscores, and dots. A term may be negated with a preceding !
.
如果构建约束中有空格, 那么就是 or 关系; 如果是逗号分隔, 那么就是 and 关系; !
表示 not。例如:
// +build linux,386 darwin,!cgo
就是表示 (linux and 386) or (darwin and (not cgo))
而且 Go 还支持多行的构建约束, 多行之间是 and 关系, 例如:
// +build linux darwin
// +build 386
就是表示 (linux or darwin) and 386
Go 官方还定义了常用的一些约束:
- 限制目标操作系统, 也就是要和 runtime.GOOS 一致
- 限制目标架构平台, 也就是要和 runtime.GOARCH 一致
- GC 或者 GCCGO 等编译器支持的约束
- cgo 约束, 也就是说如果支持 cgo 的话, 就可以参与编译
- 限制 go 版本, go1.1 表示从 go1.1 开始向前兼容; go1.2 表示从 go1.2 开始向前兼容;...
- 自定义的约束
如果你想临时让某个文件不参与编译, 可以添加注释约束: // +build ignore
.
Go 官方定义里还表示可以自定义约束, 那么这个可以这么使用呢? 了解 Go 的人都知道 Go 内置了 testing 库和单元测试框架, 跑跑一般的单元测试还是可以, 但是如果要做一些简单的集成测试就会出现很多麻烦了, 因为 go test 命令默认就是跑最基本的单元测试。那么这么只执行集成测试的代码呢? 其实这就可以借助自定义约束来实施。例如我们可以在需要进行集成测试的 .go 文件头加上 // +build integration
注释, 然后运行 go test -tags="integration"
就可以只运行我们的集成测试代码了。
虽然可以通过注释的方式对构建进行了约束, 但是文件名的构建约束反而显得更不错, 至上在工程文件目录里看着一目了然。文件名前缀只要含有 _GOOS
, _GOARCH
, _GOOS_GOARCH
的就可以。例如 xxx_linux.go, yyy_windows_amd64.go, zzz_386.go 等等。
相关资料链接:
Go official doc#Build Constraints
Binary Only Packages
Using Go’s build constraint tag to help build mock services for service testing
Golang | cmd - go build 构建约束