Knaph

A Makefile for the Toolchain

You now have a tool for formatting, a tool for imports, and a checker for likely bugs — each with its own command and flags. Go projects put them behind make, so nobody has to remember any of it. Open the Makefile in almost any Go repository and you'll find the same handful of target names: build, fmt, vet, clean.

.DEFAULT_GOAL := build
 
.PHONY: tools
tools:
	go install golang.org/x/tools/cmd/goimports@latest
 
.PHONY: fmt
fmt:
	goimports -w .
 
.PHONY: vet
vet:
	go vet main.go
 
.PHONY: build
build: fmt vet
	go build main.go
 
.PHONY: clean
clean:
	rm -f main

Run make with no arguments and it does the whole chain, because .DEFAULT_GOAL points at build and build lists fmt and vet as prerequisites — make runs those first:

goimports -w .
go vet main.go
go build main.go

If vet finds something, make stops there and never reaches the build:

goimports -w .
go vet main.go
main.go:7:20: fmt.Printf format %d has arg name of wrong type string
make: *** [vet] Error 1

That's the real value: one word, and a mistake can't slip past into a binary. Each target still works on its own — make fmt, make vet, make tools on a fresh machine.

Two pieces of Make worth naming:

  • .PHONY marks a target that isn't a file. Make normally treats a target as a filename and skips it when that file looks up to date, so without .PHONY your build target would quietly stop working the day a file named build appeared.
  • Every recipe line must start with a real tab, not spaces. Make rejects the file with missing separator otherwise, and editors convert tabs silently. This catches everyone exactly once.

These targets name main.go directly, because commands like go vet ./... need a go.mod and you don't have one yet. Once you do, the same targets grow to cover the whole project — and staticcheck and golangci-lint, which both need a module too, drop straight in beside vet.

Sign in to track your progress through this course.

Ask an AI

Open a ready-made prompt in ChatGPT or Claude — just press Enter.

SummarizeChatGPTClaude
Ask me questionsChatGPTClaude
Let's learn togetherChatGPTClaude