条件编译在 Go 里靠“文件级”的约束完成:文件名后缀与文件顶部的 //go:build。这篇不靠记忆,用 go list 与 go run 在不同平台与标签下看哪些文件被选中,再列出 tag 全集,最后看 go vet 能帮你抓住哪些书写错误。
GOOS=darwin GOARCH=arm64:
选中:cpu_arm64.go family_unix.go feat_release.go main.go os_darwin.go windows.go
排除:cpu_amd64.go cpu_other.go family_win.go feat_debug.go old_style.go os_linux.go os_windows.go tool.go
GOOS=linux GOARCH=amd64:
选中:cpu_amd64.go family_unix.go feat_release.go main.go old_style.go os_linux.go windows.go
排除:cpu_arm64.go cpu_other.go family_win.go feat_debug.go os_darwin.go os_windows.go tool.go
GOOS=linux GOARCH=arm64 -tags debug:
选中:cpu_arm64.go family_unix.go feat_debug.go main.go old_style.go os_linux.go windows.go
排除:cpu_amd64.go cpu_other.go family_win.go feat_release.go os_darwin.go os_windows.go tool.go
GOOS=windows GOARCH=amd64:
选中:cpu_amd64.go family_win.go feat_release.go main.go os_windows.go windows.go
排除:cpu_arm64.go cpu_other.go family_unix.go feat_debug.go old_style.go os_darwin.go os_linux.go tool.go
GOOS=linux GOARCH=386:
选中:cpu_other.go family_unix.go feat_release.go main.go old_style.go os_linux.go windows.go
排除:cpu_amd64.go cpu_arm64.go family_win.go feat_debug.go os_darwin.go os_windows.go tool.go一个文件参与编译,要同时满足:文件名后缀(_linux、_arm64、_linux_arm64)与顶部的 //go:build 表达式。名字里没有下划线的 windows.go 不算后缀,所以到处都选中;_test.go 只在测试时参与。上表可以直接当成检查清单:改一个文件名或约束之后,用 go list -f '{{.GoFiles}}' 在目标平台上看一遍。
go run .:文件名后缀 _darwin | 文件名后缀 _arm64 | 正式版本 | unix 家族 go run -tags debug .:文件名后缀 _darwin | 文件名后缀 _arm64 | debug 版本 | unix 家族 go run tool.go(显式给出的文件,//go:build ignore 不再生效):go run tool.go 才会用到
//go:build ignore 是写“只供 go run 的小工具”的惯用法:按包构建时它不参与,显式 go run tool.go 时命令行给出的文件直接使用,不再检查约束。
ReleaseTags 共 27 个,最早 go1.1,最近 go1.24 go1.25 go1.26 go1.27 BuildTags | ToolTags | CgoEnabled = []|[goexperiment.regabiwrappers goexperiment.regabiargs goexperiment.jsonv2 goexperiment.greenteagc goexperiment.randomizedheapbase64 goexperiment.sizespecializedmalloc arm64.v8.0]|false CGO_ENABLED=1:ToolTags | CgoEnabled = [goexperiment.regabiwrappers goexperiment.regabiargs goexperiment.jsonv2 goexperiment.greenteagc goexperiment.randomizedheapbase64 goexperiment.sizespecializedmalloc arm64.v8.0]|true GOOS/GOARCH 是否满足 unix 标签:darwin/arm64 满足;linux/amd64 满足;windows/amd64 不满足;freebsd/amd64 满足;netbsd/amd64 满足;openbsd/amd64 满足;solaris/amd64 满足;aix/ppc64 满足;ios/arm64 go list 报错(ios/arm64 requires external (cgo) linking, but cgo is not enabled);android/arm64 满足;js/wasm 不满足;plan9/amd64 不满足
满足 //go:build go1.21 这样的版本约束靠 ReleaseTags;unix 是一个汇总标签(go help buildconstraint 写的是 “if GOOS is a Unix or Unix-like system”),省得枚举所有类 unix 系统。上面的清单是在本机逐个 GOOS 用 go list 实测的;ToolTags 里出现的几个 goexperiment.*(如 jsonv2、greenteagc)说明这个工具链默认开了这些实验特性,具体含义本文没有展开。
go vet:rc=1
a.go:3:1: misplaced //go:build comment
b.go:2:1: +build lines do not match //go:build condition
go list(darwin 本机):选中 | 排除 = [a.go]|[b.go c.go]最常见的两个错误:把 //go:build 写到 package 之后(不生效,还没有编译错误,只有 vet 会告诉你),以及新旧两种写法互相矛盾。gofmt 会自动让两者保持一致;建议新代码只写 //go:build。