构建约束

//go:build go1.17 || (linux && 386) || (darwin && !cgo)

01 什么是构建约束?

构建约束(build constraint)也叫做构建标记(build tag),以文件名或注释形式,标记当前文件是否参与构建过程。

文件名形式的构建约束比较简单,暂且按下不表。Go 中一直存在一种 // +build 形式的构建约束语法,标记间的组合关系通过空格、逗号或多行来表示,例如:

// +build go1.17 linux,386 darwin,!cgo

这种语法示意性非常差!哪怕是下面这种多行形式,示意性也并没有得到多少改善。

// +build go1.17
// +build linux,386
// +build darwin,!cgo

而 go1.17 引入的新语法示意性就非常棒了。上面的约束用新语法表示成:

//go:build go1.17 || (linux && 386) || (darwin && !cgo)

新的约束语法可以理解为,标签表达式为真,当前文件参与构建

02 文件名形式构建约束

文件名按照以下模式构成构建约束:

*_GOOS
*_GOARCH
*_GOOS_GOARCH

例如 source_windows_amd64.go 只会在 windows 操作系统中 amd64 架构下参与构建。

想要知道 GOOS 与 GOARCH 所有可选项,可通过 go tool dist list 查看。

03 注释形式构建约束

使用要点:

构建标记 xxx,可用以下几种类型的备选词:

标记之间的组合关系用 ||&&!() 表示。

04 单聊“自定义 tag”

纵览众多开源项目,重多构建约束注释中 //go:build xxx 你或许遇到过许多奇奇怪怪的标记 xxx,那些陌生的标记往往是自定义 tag 。例如:依赖注入工具 wire 的 wire.go 文件有 //go:build wireinject 这条约束。wireinject 便是 wire 这个工具自己定义的 tag 。在构建过程中会带到构建指令中 go build -tags wireinject

下面举个例子,应用自定义 tag:

app/main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "fmt"

var features = []string{
	"Free Feature #1",
	"Free Feature #2",
}

func main() {
	for _, f := range features {
		fmt.Println(">", f)
	}
}

app/pro.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//go:build pro

package main

func init() {
	features = append(features,
		"Pro Feature #1",
		"Pro Feature #2",
	)
}

构建 app

go build -tags pro

输出结果:

./app

> Free Feature #1
> Free Feature #2
> Pro Feature #1
> Pro Feature #2

app 这款应用中我们自己定义了一个名为 pro 的构建标签,当我们需要给 app 增加 pro 版本的功能时,无需修改代码,通过在构建时加 -tags pro 即可完成。

05 参考资料