Go 编译标签 build tag

在 Go 中,build tag 是添加到代码中第一行,来标识编译相关信息的,build tag 决定了当前文件是否会被当前 package 所包含,用于限制一整个文件是否应该被编译入最终的二进制文件,而不是一个文件中的部分代码片段。

Go 编译标签build tag语法如下:

1
// +build [tag]
  • build tags 文件顶部附近,前面只能有空行和其他行注释。
  • 编译标记必须出现在 package 子句之前,并且为了与包文档区分开来,它必须后跟一个空行。

当在一个包中使用多个标签时会使用 bool 逻辑进行交互,具体取决于我们如何进行声明的。

Build tags 遵循以下三个规则:

  • 以空格分隔的标签将在OR逻辑下进行解释。
  • 逗号分隔的标签将在AND逻辑下进行解释。
  • 每个术语都是一个字母数字单词,如果前面有!它意味着它被否定。

给定标签:

1
// +build tag1 tag2

OR 解释是,如果在执行 build 构建命令时存在 tag1 或 tag2,则将包含此文件。

如果我们使用标签:

1
// +build tag1, tag2

解释是 tag1 且(AND) tag2 必须存在于 build 构建命令中,我们的文件才能包含在编译中。

如果我们使用标签

1
 // +build !tag1

解释是,非 tag1,我们的文件才会 build 编译

我们新建一个 buildtag 文件夹,并在文件夹下新建如下4个空文件,如下:

1
2
3
4
5
6
.
├── dev.go
├── main.go
├── prod.go
├── test.go
└── without.go

我们打开 main.go 输入代码如下:

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

import "fmt"

var configArr []string

func main() {
	for _, conf := range configArr {
		fmt.Println(conf)
	}
}

我们打开 dev.go 输入代码如下:

1
2
3
4
5
6
// +build dev
package main

func init() {
	configArr = append(configArr, "mysql dev")
}

我们打开 prod.go 输入代码如下:

1
2
3
4
5
6
7
// +build prod

package main

func init() {
	configArr = append(configArr, "mysql prod")
}

我们打开 test.go 输入代码如下:

1
2
3
4
5
6
// +build test1
package main

func init() {
	configArr = append(configArr, "mysql test")
}

我们打开 without.go 输入代码如下:

1
2
3
4
5
6
7
// +build !without

package main

func init() {
	configArr = append(configArr, "mysql without")
}

我们使用

1
go build

在文件夹里生成了二进制执行文件 buildtag,我们执行一下:

1
➜ ./buildtag 

输出:

1
mysql without

我们使用

1
go build  -tags "dev" 

在文件夹里生成了二进制执行文件 buildtag,我们执行一下:

1
➜ ./buildtag 

输出:

1
mysql dev

我们使用

1
go build  -tags "dev prod" 

在文件夹里生成了二进制执行文件 buildtag,我们执行一下:

1
➜ ./buildtag 

输出:

1
2
mysql dev
mysql prod
1
//go:build

☝️是 Go 1.17 中引入的新条件编译指令格式。它旨在替换

1
// +build

指令。那么为何要采用新的格式呢?对比一下新旧格式的区别就知道了👇

1
2
// go:build linux && amd64 || darwin
// +build linux,amd64 darwin

go:build这种格式,对 coder 来说,更容易理解其逻辑组合,与//go:embed//go:generate这些命令相比较,格式上进行了统一。