Make's terse errors are easy once you know how to ask it what it's thinking.
When a build does something unexpected, ask make directly:
make -nprints the commands it would run, without running them.make --trace(4.0+) prints each recipe with the reason it ran, for example "target 'build/x.o' does not exist" or "prerequisite 'src/x.c' is newer than target".make --debug=b(basic) or--debug=v(verbose) explains its decisions;--debug=iadds implicit-rule search, the answer to "why was that weird built-in rule used?".make -pdumps the whole database after reading: every variable with its origin, and every rule. Combine with-qor a fake target to avoid building:make -pq | less.$(info VAR=$(VAR)),$(warning ...)print during the read phase;$(error ...)stops.make -B -n targetshows the complete recipe chain for a target.
Common messages decoded:
missing separator: a recipe line not indented with a tab, or a stray line of text.No rule to make target 'X', needed by 'Y': a prerequisite doesn't exist and no rule (explicit or pattern) can create it; often a typo, a deleted header still in a.dfile, or a pattern rule whose source path doesn't match.Circular X <- Y dependency dropped: the graph has a cycle.target 'X' given more than once in the same ruleoroverriding recipe: two recipes for one target, usually a copy-paste mistake.
$(origin VAR) and $(flavor VAR) tell you where a variable came from and whether it's recursive or simple.
Example
# Makefile with a mistake: header path is wrong
build/app.o: src/app.c src/app.hh
cc -c $< -o $@
$(info CC is '$(CC)' from $(origin CC), flavor $(flavor CC))
# $ make build/app.o
# CC is 'cc' from default, flavor recursive
# make: *** No rule to make target 'src/app.hh', needed by 'build/app.o'. Stop.
# after fixing it:
# $ touch src/app.c && make --trace build/app.o
# Makefile:3: update target 'build/app.o' due to: src/app.c
# cc -c src/app.c -o build/app.o
# $ make -p -f /dev/null | grep -A2 '^%.o: %.c'
# %.o: %.c
# # recipe to execute (built-in):
# $(COMPILE.c) $(OUTPUT_OPTION) $<Remember
- --trace says why each recipe ran; -n shows what would run
- -p dumps all variables and rules, including built-ins
- 'No rule to make target' means a missing file with no rule to create it
Watch out for
- Guessing at rebuild problems instead of running make --trace or --debug=b, which states the reason.