Golang: Build: Constraints

31st August 2021 at 2:48pm

Go 提供了按条件编译某文件的能力,通过在 Go 文件中加入一行特殊的注释来实现:

//go:build (darwin && cgo) || linux
// +build darwin,cgo linux

packaget getIP

func main() {
    // ...
}

其中这两行 build 注释是等价的:

  • //go:build 是 Go 1.17 及以后的语法。只能存在一行
  • //+build 是 Go 1.16 及之前的语法。可以存在多行,多行间的关系是 AND。1.17 及以上的 gofmt 遇到这种老语法时,会自动生成新的语法

你可以看到里面用了这些变量 darwin, cgo 等。可用的变量来源于:

  • GOOS 的值,比如 linux, darwin, windows
  • GOARCH 的值,比如 386, amd64, arm
  • Go 编译器,gcgccgo
  • cgo,如果 CGO_ENABLED 被设置成 1
  • Go 版本号,如 go1.1 表示 Go 1.1 版本及以上,go1.12 表示 Go 1.12 版本及以上
  • 其他在 go build -tags 中指定的 tag

另外,如果你的 go 文件名,除去扩展名和 _test 后缀后,满足这些 pattern:

  • *_GOOS,比如 a_windows.go a_windows_test.go
  • *_GOARCH,比如 a_arm.go math_386.s
  • *_GOOS_GOARCH,比如 a_linux_arm.go

表示隐式的 build constraints。

使用 -tags 的例子

google/wire 使用了 -tags 的能力。

在编写 wire.go 时你需要指定 wireinject

//go:build wireinject
// +build wireinject

使用 wire CLI,它会通过 -tags 机制,扫描目录下的带 wireinject 的文件,并生成 wire_gen.go,其中带有这样的头:

//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject

这使得 go 在正常编译此项目时,会跳过 wire.go 而仅使用 wire_gen.go

参考

Build constraints 注释的语法在 Go 1.17 版本后有变化:

JetBrains 也有一个 文档 描述此功能。