How it works
The explanation, with examples you can run.
What a build does
The go command finds the packages your main package imports, compiles each one (caching the results, so rebuilds are fast) and hands the object files to the linker. Imports are checked by the compiler: an unused import is an error, which keeps the dependency graph honest.
package main import ( "fmt" "runtime") func main() { fmt.Println(runtime.Version(), runtime.GOOS+"/"+runtime.GOARCH) fmt.Println("CPUs:", runtime.NumCPU(), "GOMAXPROCS:", runtime.GOMAXPROCS(0)) fmt.Println("goroutines already running:", runtime.NumGoroutine())}On an Apple-silicon Mac with Go 1.26 this printed go1.26.1 darwin/arm64, the CPU count twice, and one goroutine: the one running main.
The runtime is part of your program
Go's runtime plays roughly the part libc plays for C, but it does much more. It schedules goroutines onto operating-system threads, grows and shrinks their stacks, runs the concurrent garbage collector, implements maps and channels, and prints the stack traces you see on a panic. It starts before main and its background work runs on threads it manages. That is why a trivial program is a couple of megabytes: the FAQ points out that the binary also carries the type information needed for reflection and panics.
Static, mostly
The gc linker links Go code statically. Two things can pull in the system C library dynamically:
- cgo. If cgo is enabled, some standard packages (DNS lookups in
net, user lookups inos/user) may use the C versions. Building withCGO_ENABLED=0uses the pure-Go ones. - The platform. macOS requires programs to go through
libSystem, so Mac binaries always link it.
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app ./cmd/appfile app # ELF 64-bit LSB executable, x86-64, statically linkedgo version -m app # the Go version and module versions it was built fromA static Linux binary can be copied into a scratch or distroless container image with nothing else in it, apart from CA certificates if it makes TLS calls and time-zone data if it needs it (or import time/tzdata to embed it).
Cross-compiling
Because the compiler and linker are written in Go and the standard library has no C dependency when cgo is off, building for another target is just GOOS and GOARCH. go tool dist list prints every supported pair. Build tags and file suffixes such as _linux.go or _arm64.go select platform-specific files.
go run, go install, go vet
go run .builds into a temporary directory and runs the result: handy, but it is still a full compile.go installputs the binary in$GOBIN(by default$HOME/go/bin).go vetruns static checks that catch real bugs, such asPrintfformat mismatches, copied locks andAddcalled inside aWaitGroupgoroutine.go testruns a subset of them automatically.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.