Compiler & Linker Flags — Junior¶
1. Why flags exist¶
go build knows how to build a Go program out of the box. But sometimes you want to:
- Make the binary smaller.
- Inject a version string.
- Debug a tricky bug.
- See what optimizations the compiler did.
- Build for a different operating system.
Flags let you do all of that. They're passed to go build (and most other go subcommands).
2. The most common: -ldflags="-s -w"¶
-s strips the symbol table; -w strips debug info (DWARF). Result: ~10–20% smaller binary. Useful for release builds, not for debugging.
3. Injecting a version string¶
// in main.go
package main
import "fmt"
var version = "dev"
func main() {
fmt.Println("version:", version)
}
The -X flag sets a string variable at link time. Used everywhere to embed git commits, build dates, environment names.
4. Combining¶
go build \
-trimpath \
-ldflags="-s -w -X main.version=$(git rev-parse --short HEAD)" \
-o app ./cmd/app
Common release-build incantation:
-trimpath: cleans paths from the binary.-s -w: strips debug info.-X main.version=...: embeds the version.
5. Debug builds¶
-N disables optimizations, -l disables inlining. With these, your debugger (delve) can step through code at the source level cleanly. Don't ship -N/-l binaries to production — they're slower.
6. See what's happening: -x¶
Prints every command go build executes. Useful when you're trying to understand the toolchain or debug a strange build failure.
7. See optimization decisions: -gcflags='-m'¶
Prints what the compiler decided about escapes and inlining for each file:
./main.go:10:6: can inline doIt
./main.go:11:9: inlining call to fmt.Println
./main.go:12:6: moved to heap: x
-m=2 is more detailed; -m=3 is exhaustive.
8. Build for another platform¶
GOOS=linux GOARCH=amd64 go build -o app-linux ./cmd/app
GOOS=windows GOARCH=amd64 go build -o app.exe ./cmd/app
GOOS=darwin GOARCH=arm64 go build -o app-mac ./cmd/app
These aren't -flags per se — they're environment variables — but they go together with build flags constantly.
9. -race¶
Enables the race detector. Use this in tests and CI. The runtime overhead is 2–20×, so don't ship -race binaries.
10. A few flags worth remembering¶
| Flag | When |
|---|---|
-ldflags="-s -w" | Release builds |
-ldflags="-X pkg.var=val" | Inject build-time values |
-trimpath | Reproducible builds |
-gcflags='all=-N -l' | Debug builds |
-gcflags='-m' | See escape decisions |
-race | Test/debug for data races |
-x | Show toolchain commands |
-pgo=auto | Profile-guided optimization |
11. Summary¶
Flags adjust how go build produces a binary: smaller (strip), debuggable (no optimizations), versioned (link-time injection), profiled (PGO), or instrumented (race). Most teams memorize one release-build invocation and one debug-build invocation, and use IDE shortcuts for the rest.
Further reading¶
go help buildgo help buildflags