Build Constraints & Platform-Specific Files
Build constraints tell the Go toolchain which source files belong in a given build.
Search across all documentation pages
Build constraints tell the Go toolchain which source files belong in a given build.
You express them with //go:build lines, file suffixes, and custom tags passed via -tags.
The result is one module tree that compiles different implementations per OS, architecture, or feature flag.
Before Go 1.17, constraints used // +build comment lines.
Modern code should use //go:build as the first line of a file (optionally followed by blank line and package).
The toolchain evaluates constraints against GOOS, GOARCH, and custom tags.
Files without constraints are always included; platform-specific files should provide fallbacks or live behind matching tags only.
Quick-reference recipe card - copy-paste ready.
//go:build linux && amd64
package sysfs
// linux_amd64 implementationgo build -tags=integration ./...
GOOS=windows GOARCH=arm64 go build ./...When to reach for this:
syscall, x/sys) without runtime if runtime.GOOS sprawl.example.com/net/
listen.go
listen_unix.go
listen_windows.go
// listen.go - all platforms
package net
func DefaultListenConfig() ListenConfig {
return ListenConfig{}
}
type ListenConfig struct {
ReusePort bool
}// listen_unix.go
//go:build unix
package net
func (c ListenConfig) platformReuse() bool { return c.ReusePort }// listen_windows.go
//go:build windows
package net
func (c ListenConfig) platformReuse() bool { return false } // SO_REUSEPORT unlike Windows// demo/main.go
package main
import (
"fmt"
"example.com/net"
)
func main() {
cfg := net.DefaultListenConfig()
cfg.ReusePort = true
fmt.Println("reuse supported:", cfg.platformReuse())
}What this demonstrates:
unix is a predefined tag covering Linux, BSD, macOS, and other Unix-like GOOS values.go build collects all .go files in the package directory.GOOS, GOARCH, and -tags values form the evaluation environment.| Term | Meaning |
|---|---|
GOOS value | linux, windows, darwin, freebsd, ... |
GOARCH value | amd64, arm64, wasm, ... |
unix | Unix-like platforms (see go tool dist list) |
gc / gccgo | Compiler toolchain |
cgo | cgo enabled for this build |
| Suffix pattern | Implied constraint |
|---|---|
_linux.go | GOOS=linux |
_windows.go | GOOS=windows |
_amd64.go | GOARCH=amd64 |
_linux_amd64.go | linux AND amd64 |
Suffix rules combine with explicit //go:build lines when both are present.
//go:build (linux || darwin) && amd64
//go:build !windows
//go:build integration && !short
Use parentheses for precedence.
go list -tags=integration -f '{{.GoFiles}}' ./...Inspect selected files before debugging "undefined symbol" build failures.
linux leaves other GOOS without required functions. Fix: add default.go or !linux file with portable code._foo.go plus conflicting //go:build confuses readers. Fix: prefer one style per file.linux/amd64 hides broken Windows files. Fix: cross-compile GOOS=windows in CI at minimum.go test without -tags and skip suites silently. Fix: document tags in Makefile and README.// +build only - still works but gofmt rewrites to //go:build in modern toolchains. Fix: migrate on touch.| Alternative | Use When | Don't Use When |
|---|---|---|
runtime.GOOS switch | Tiny behavioral differences, no special imports | File needs OS-only imports |
| Separate packages per platform | Large divergent implementations | Simple one-function deltas |
| WASM/browser build tags | JS-specific entrypoints | Server-only code |
| Build tags for features | Optional paid/enterprise modules | Simple config toggles better as runtime flags |
The constraint line must appear before the package clause (only comments and blank lines may precede it).
Tags apply to .go sources.
Use separate embed or asset trees with tagged Go files that reference them.
Common pattern: //go:build integration && !short so default go test -short skips slow tests.
Tags apply per invocation.
go build and go test each need the same -tags when you expect identical file sets.
Yes.
//go:build cgo selects files only when cgo is enabled for the build.
Mobile targets use GOOS values like ios and android with their own tag sets.
Verify with go tool dist list for your Go version.
Run go list -f '{{.GoFiles}} {{.IgnoredGoFiles}}' ./package to see ignored sources.
Yes.
_test.go files follow the same build constraint rules as production files.
Only one //go:build constraint line (possibly with boolean operators).
Do not stack multiple separate constraint comments.
Tags apply per module build inside the workspace.
Each module's tagged files are evaluated independently.
.s filesStack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026