go条件编译
go支持两种条件编译的实现方式
编译标签(build tags)
//go:build !windows
和// +build !windows
文件后缀
条件编译的应用场景
- 平台适配:不同平台的操作系统和 CPU 架构可能有不同的特性和限制,需要针对不同平台编写不同的代码。通过条件编译,可以根据不同平台选择不同的代码进行编译,从而提高程序的性能和稳定性。
- 调试信息:在开发和调试过程中,需要添加一些调试信息来辅助开发人员定位问题。但是,在发布版本时这些信息不应该包含在内。使用条件编译,可以在开发和调试阶段包含调试信息,在发布版本中去除这些信息。
- 功能开关:有些功能可能只在特定的场景下使用,而在其他场景下不需要。使用条件编译可以根据编译标记开启或关闭这些功能,从而减少程序的代码量和复杂度。
- 性能优化:有些代码可能只在特定的环境下才能发挥最优性能。使用条件编译,可以根据编译标记选择不同的代码实现,从而提高程序的性能。
# 编译标签(build tags)
# 1. //go:build
和 // +build
的区别
// +build
编译条件的注释和package 语句之间一定要隔一行。不然无法识别编译条件,编译条件支持“非”逻辑,比如某个文件在非 windows 环境下编译//go:build
是Go 1.17中引入的新的条件编译指令,旨在取代// +build
指令,因为新语法带来了一些关键改进:- 与其他现有Go指令和pragma的一致性,例如
//go:generate
- 支持标准布尔表达式,例如
//go:build foo && bar
,而旧的// +build
注释的语法不那么直观。例如,AND用逗号// +build foo,bar
和或空格// +build foo bar
表示 - 它由
go fmt
支持,它将自动修复指令在源文件中的错误位置,从而避免常见错误,如在指令和包语句之间不留空行。
- 与其他现有Go指令和pragma的一致性,例如
在Go 1.N中:
- 生成将开始优先选择
//go:build
行进行文件选择。如果文件中没有//go:build
,那么任何// +build
行仍然适用。 - 如果Go文件包含
//go:build
而没有// +build
,则构建将不再失败。 - 如果Go或程序集文件中包含
//go:build
太晚,则生成将失败。Gofmt将把错位的//go:build和//+build行移到文件中的正确位置。 Gofmt
将使用与其他Go布尔表达式(所有&&
和||
运算符周围的空格)相同的规则格式化//go:build
行中的表达式。- 如果文件只包含
// +build
行,则gofmt
将在其上方添加一个等效的//go:build
行。 - 如果一个文件同时包含
//go:build
和// +build
行,则gofmt
将考虑//go:build
是真理的来源,并更新// +build
行以匹配,从而保持与早期版本的Go的兼容性。Gofmt
还将拒绝被认为太复杂而无法转换为// +build
格式的//go:build
行,尽管这种情况很少见。(注意此项目符号开头的“If”。Gofmt
不会将// +build
行添加到只有//go:build
.的文件中) buildtags
签入go vet
将添加对//go:build
约束的支持。当Go源文件包含具有不同含义的//go:build
和// +build
行时,它将失败。如果检查失败,可以运行gofmt -w
。
- 生成将开始优先选择
# 2. 使用说明
基础语法
// +build <tag1> <tag2> <tag3> ... 或者是 //go:build <tag1> <tag2> <tag3> ... 编译的时候 go build -tag1 mytag2 mytag3
1
2
3
4
5
6<tag>
可以是操作系统、CPU 架构、编译标记等,多个标记之间用空格分隔或者都好分隔。- 编译标签由空格分隔的编译选项(options)以”或”的逻辑关系组成
- 每个编译选项由逗号分隔的条件项以逻辑”与”的关系组成
- 每个条件项的名字用字母+数字表示,在前面加!表示否定的意思
- 不同tag域之间用空格区分,他们是OR关系
- 同一tag域之内不同的tag用都好区分,他们是AND关系
- 每一个tag都由字母和数字构成,!开头表示条件“非”
// +build linux darwin // +build 386
1
2一个源文件里可以有多个编译标签,多个编译标签之间是逻辑”与”的关系
关系
空格表示:AND 逗号表示:OR !表示:NOT 换行表示:AND
1
2
3
4
内置 tag
指定操作系统:如
darwin
、linux
、windows
等,对应 runtime.GOOS 的值。指定CPU 架构:如
amd64
、arm
、386
等,对应 runtime.GOARCH 的值。指定编译器:例如:gccgo、gc,是否开启CGO,cgo。
指定Go 版本:例如:go1.19、go1.20 等。
// +build ignore,编译时自动忽略该文件
demo
// +build linux,arm !darwin 表示该文件在 Linux 平台且 ARM 架构下编译,但不在 Darwin 平台下编译。
1
2
3
自定义tag,
// +build mytag
编译的时候go build -tags mytag
才会编译此文件
# 3. 注意事项
// +build
或者//go:build
指令必须放在文件的开头。// +build
或者//go:build
指令只能出现一次,且只能针对整个文件进行编译,不能对单独的函数或变量进行编译。- 标记中的操作系统和 CPU 架构必须使用官方定义的名称,否则编译器无法识别。
- 在编写代码时,应该尽量避免使用条件编译,以保持代码的简洁和易读性。
- 在使用编译标记时,应该尽量使用官方定义的标记,避免与其他库或框架的标记冲突。
# 文件后缀
这个方法通过改变文件名的后缀来提供条件编译,这种方案比编译标签要简单很多
go/build可以在不读取源文件的情况下就可以决定哪些文件不需要参与编译。
简单来说,就是源文件包含后缀:$GOOS.go,那么这个源文件只会在这个平台下编译,$GOARCH.go也是如此。
demo
mygo_freebsd_arm.go // only builds on freebsd/arm systems mygo_plan9.go // only builds on plan9
1
2
上次更新: 2023/09/01, 22:31:42