Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Make — House Rules

Who this is for — anyone adding a target to a project Makefile. Reference material: grep it, don’t read it.

Read this if — you need the ⚙️ sentinel or the self-documenting help recipe.
Skip this if — you want the reasoning behind the subset; that is How to Read the House Rules.

Takeaways

  1. help is the first target; bare make prints usage.
  2. The ⚙️ sentinel replaces per-target .PHONY bookkeeping.
  3. Express order as dependencies, and always run the locally built binary.

GNU Make is the ultimate tool for orchestrating local-first development workflows. By enforcing a clean structure and a few modern conventions, Makefiles stay maintainable and self-documenting.


1. Structure & Defaults

  • The first target in the Makefile must always be help. Running make alone should display usage instructions.
  • Add a blank line between targets to separate them visually.

2. Variables

  • Use := for immediate variable assignment.
  • Use ?= for variables that should be overridable by environment variables (e.g. PREFIX ?= /usr/local).
  • Align = signs for clean reading:
    BINARY  := books
    BUILD   := build
    PREFIX  ?= /usr/local
    

3. The ⚙️ Sentinel Trick

Declaring .PHONY rules manually for every target is tedious and prone to omissions. We use the Unicode gear ⚙️ sentinel to implicitly make all targets phony:

.PHONY: ⚙️  # make all commands phony

Adding ⚙️ as a dependency to every target ensures that it always runs:

build: ⚙️  # compile the application
	go build -o $(BINARY) .

4. Self-Documenting Help

We scrape comments starting with ⚙️ and description comments (# or ##) to build a dynamic help target:

help: ⚙️  ## show this help
	@grep -E '^[a-zA-Z_-]+:.*⚙.*#+' $(MAKEFILE_LIST) | \
	awk 'BEGIN {FS = ":.*#+ "}; {printf "  %-15s %s\n", $$1, $$2}'

Any target defined with name: ⚙️ ## description — or with a single # — will be automatically formatted and listed when the user types make.

5. Dependency Chains

Ensure action targets build the application first:

build: ⚙️  ## build target
	go build -o $(BINARY)

run: ⚙️ build  ## run the locally-built binary
	./$(BINARY)

Always invoke the locally-built binary (e.g., ./$(BINARY)), never the global one on the $PATH.