Go 构建约束(build constraints)的用法
Go 编译器在构建时可以根据条件选择哪些文件参与编译,这个机制叫构建约束(build constraints),也叫构建标记(build tag)。常见的使用场景是跨平台代码:针对 Linux 和 Windows 分别写不同的实现,编译时只有当前平台对应的文件会被编译进去。
构建约束有两种方式:文件名后缀和 build tag 注释。
文件名后缀
最简单的方式,直接在文件名里带上平台信息,格式是:
filename_GOOS.go
filename_GOARCH.go
filename_GOOS_GOARCH.go
比如 net_linux.go 只在 Linux 下编译,net_windows.go 只在 Windows 下编译。Go 标准库里大量使用这种方式,看一眼 os 包的源码就能看到。
这种方式适合跨平台实现,简单直接,不需要写任何注释。
Build tag 注释
更灵活的方式是在文件顶部写注释标记,可以组合多个条件,也可以用自定义 tag。
新语法(Go 1.17+)
//go:build linux && amd64
注意 // 和 go 之间没有空格,这和普通注释不一样。
用标准的布尔运算符组合条件:
//go:build linux || darwin
//go:build !windows
//go:build (linux || darwin) && amd64
旧语法(Go 1.16 及之前)
// +build linux,386
旧语法用逗号表示 AND,空格表示 OR,多个条件可以写多行(多行之间是 AND 关系)。这套规则很反直觉,容易写错:
// +build linux,386 darwin,!cgo
上面这行的意思是 (linux AND 386) OR (darwin AND NOT cgo),不看文档很难一眼看懂。
Go 1.17 之后,gofmt 会自动在旧语法下面补一行新语法,保持兼容:
//go:build !windows && !plan9
// +build !windows,!plan9
Go 1.18 开始,go fix 工具会直接移除旧的 // +build 注释。现在写新代码直接用 //go:build 就行。
写法要求
build tag 必须放在文件最顶部,在 package 声明之前,和下面的代码之间要有一个空行:
//go:build linux
package main
少了那个空行,Go 编译器不会把它识别为构建约束。
自定义 tag
除了平台和架构,还可以定义自己的 tag,在编译时通过 -tags 传入:
//go:build use_feature
编译时:
go build -tags use_feature
不带 -tags use_feature 时这个文件会被忽略,带了才参与编译。多个 tag 用空格分隔:
go build -tags "feature1 feature2"
这个用法常见于功能开关、调试模式、或者区分 CE/EE 版本之类的场景。
tag 可以用的值
- 操作系统:
linux、darwin、windows等(对应GOOS) - 架构:
amd64、arm64、386等(对应GOARCH) - 编译器:
gc、gccgo - CGO:
cgo - Go 版本:
go1.21、go1.22等 - 自定义:任意字符串,通过
-tags传入 ignore:约定俗成的忽略标记,让文件永远不参与编译