Build Constraints — Specification¶
Focus: Precise reference for Go build constraints (a.k.a. build tags) — the syntax, the semantics, and the inputs that determine whether a file is part of a build.
Sources: -
go help buildconstraint-cmd/gobuild constraints: https://pkg.go.dev/cmd/go#hdr-Build_constraints - Go 1.17 design: https://go.dev/design/draft-build-constraints
1. What a build constraint is¶
A build constraint is a line in a Go source file that tells the toolchain under which conditions the file should be compiled. If the constraint is false, the file is treated as if it didn't exist.
Two syntaxes:
Modern Go uses //go:build; the legacy // +build form is still accepted and gofmt keeps both in sync if both appear.
2. Placement rules¶
- Must appear before the
packageclause. - Separated from the
packageclause by exactly one blank line. - Each constraint line is processed independently.
- A file may contain at most one
//go:buildline.
Example:
gofmt enforces the blank line.
3. Expression grammar¶
//go:build expressions are Boolean expressions with:
- Identifiers: tag names (
linux,amd64,cgo, custom tags). - Operators:
&&,||,!, parentheses.
Legacy // +build uses different syntax:
- Comma is
AND. - Space is
OR. !prefix isNOT.- Multiple
// +buildlines are AND-ed.
Translation example: // +build linux,!cgo becomes //go:build linux && !cgo.
4. Predefined tags¶
Build constraints can reference:
| Category | Examples |
|---|---|
OS (GOOS) | linux, darwin, windows, freebsd, wasip1, js, plan9 |
Architecture (GOARCH) | amd64, arm64, arm, 386, wasm, ppc64le |
| Compiler | gc, gccgo |
| Cgo | cgo |
| Race | race |
| Mutex profiling | msan, asan |
| Go version | go1.20, go1.21, go1.22 (matches the current build's version or later) |
| Boring crypto | boringcrypto |
A complete list lives in go/build's Default.ReleaseTags.
5. File-name implicit constraints¶
Filenames ending in _GOOS.go, _GOARCH.go, or _GOOS_GOARCH.go carry implicit constraints based on the suffix:
fastpath_linux.go → //go:build linux
fastpath_linux_amd64.go → //go:build linux && amd64
fastpath_test.go → only built for `go test`
These take effect in addition to any explicit //go:build line.
6. Test file conventions¶
| Filename | Built when |
|---|---|
foo_test.go | go test; not in regular builds |
foo_linux_test.go | go test on Linux |
example_test.go | Same as *_test.go |
The _test suffix is a sharper constraint than any tag — tests never appear in go build.
7. Custom tags¶
You can define arbitrary tags:
Then build:
-tags accepts a comma-separated list. Use sparingly — feature flags as build tags lock behavior into the binary; runtime flags or env vars are usually better.
8. Multiple files for the same package¶
A package may contain files with different constraints:
mypkg/
api.go // common
api_linux.go // Linux only
api_windows.go // Windows only
api_unix.go // //go:build unix
unix is a meta-tag (Go 1.19+) that matches Linux, macOS, BSD families.
Each file is independently selected. The package is the union of files whose constraints pass.
9. Build cache interaction¶
Build caches are keyed by:
- Go source content
- Build tags (the exact
-tagsset) - Target platform (
GOOS,GOARCH) - Toolchain version
- A few env vars (
CGO_ENABLED,CC, etc.)
So go build and go build -tags=foo use disjoint cache entries. Switching frequently between tag sets keeps multiple sets warm.
10. go list interactions¶
go list -f '{{.GoFiles}}' ./... # files in the default build
go list -tags=prod -f '{{.GoFiles}}' ./... # files in the prod build
go list -e -f '{{.IgnoredGoFiles}}' ./... # files excluded by constraints
Useful for verifying which files contribute to a given build, especially when debugging "why isn't my code being compiled?" issues.
11. Constraint inheritance into vet, lint, generate¶
| Tool | Respects constraints? |
|---|---|
go build | Yes |
go test | Yes |
go vet | Yes |
go generate | Yes (uses the current GOOS/GOARCH) |
golangci-lint | Yes (with -build-tags) |
goimports | Yes |
Editor LSP (gopls) | Yes (with build.buildFlags) |
For a multi-platform package, configure your editor's gopls to include multiple tag sets, or you'll get spurious "unused import" warnings.
12. Negative constraints¶
The file is built only when cgo is disabled. Useful for providing a pure-Go fallback alongside a cgo-accelerated path:
13. The unix meta-constraint¶
Matches linux, darwin, freebsd, netbsd, openbsd, aix, dragonfly, solaris. Introduced Go 1.19. Replaces the older hand-written union.
14. Version constraints¶
The file is built when compiling with Go 1.21 or later. This is the standard way to gate code on a new language feature or stdlib API. For an older toolchain, the file is skipped entirely.
go1.X does not match Go releases before 1.X; the comparison is >=.
15. Common pitfalls¶
| Pitfall | Symptom |
|---|---|
No blank line before package | Constraint is ignored |
//go:build in middle of file | Ignored |
Forgetting +build legacy line on older Go versions | File missing on old toolchains |
Tag typo (e.g., darwin vs osx) | File never built |
| Build cache mismatches across tag sets | Mysterious "stale" builds |
16. Related references¶
go help buildconstraintgo/builddocumentation: https://pkg.go.dev/go/build- Go 1.17 release notes (
//go:buildintroduction) - Cross-compilation: 10-go-toolchain