Explain build constraints: the //go:build syntax, filename rules, and common gotchas.
Question 423MediumGo 1.22 to 1.25
Build constraints decide whether a file is part of a package for a given build. There are two mechanisms.
1. //go:build expressions (Go 1.17+). These are boolean expressions with &&, ||, ! and parentheses over GOOS, GOARCH, cgo, unix, goX.Y, compiler names and custom tags. The line must appear before the package clause, preceded only by blank lines and other comments, and gofmt places a blank line after it.
//go:build (linux || darwin) && amd64 && !purego
package fastpath
2. Filename suffixes: *_GOOS.go, *_GOARCH.go, *_GOOS_GOARCH.go, and *_test.go. For example, poll_linux_arm64.go builds only on linux/arm64.
Gotchas:
- A stray
linux.gois fine, butfoo_windows.gosilently disappears on Linux, which is confusing when the file was not meant to be OS-specific. unixis valid only in//go:build, not as a filename suffix.- A typo in a tag name is not an error. The file is just silently excluded.
- Old
// +buildlines use different syntax (space means OR, comma means AND).go fixconverts them, andgo vetreports mismatches. - Files starting with
_or.are always ignored.
Check what is actually included with go list -f '{{.GoFiles}} {{.IgnoredGoFiles}}'.
More on Modules, Packages & Tooling
- Q421What does the Go 1.24 tool directive replace, and how do you use it?
- Q422What is the difference between go get and go install pkg@version in module mode?
- Q424How would you separate integration tests from unit tests using build tags? What alternatives exist?
- Q425How does go generate work, and what are best practices around it?
- Q426What are the tradeoffs of using cgo? When would you avoid it?
- Q427What are the cgo pointer-passing rules, and how does runtime.Pinner help?