Table of Contents
Makefile Debugging
Debugging Makefiles is often about understanding why Make made certain decisions. Use flags, echo commands, and systematic investigation.
Dry-run mode (-n): Preview what Make would do without executing:
make -n make -n clean
Shows all recipes that would run, in order. Useful for verifying dependencies before running for real.
Verbose mode (-d): Print detailed debug info (heavy output):
make -d | grep -E "Considering|No need|Must remake" | head -20
Shows Make's internal reasoning: which files it's considering, which are out of date, etc. Produces lots of output.
Print variables: Use @echo to inspect values:
debug: @echo "SRCS: $(SRCS)" @echo "OBJS: $(OBJS)" @echo "CFLAGS: $(CFLAGS)"
Run with make debug. Helps verify that variables expand as expected.
Print rules: See what rules apply to a target:
make -p | grep -A 5 "main.o"
-p (print database) shows all targets and rules. Search for your target to see dependencies and recipe.
Debugging dependency issues: If a file isn't rebuilding when you expect:
make -d main.o 2>&1 | grep -E "newer|rebuild|remake"
Shows whether Make thinks main.o is up to date, and why. Look for timestamp comparisons.
Silent recipes: Recipes print commands by default. Suppress with @:
app: main.o gcc -o app main.o # prints: gcc -o app main.o quiet_app: main.o @gcc -o app main.o # prints nothing, just the output (if any)
Use @ in recipes to reduce noise. But for debugging, remove @ to see what's executing.
Echo before execution: Use @echo then the command:
app: main.o @echo "Linking..." gcc -o app main.o
Prints Linking... then runs gcc. Useful for progress messages.
Conditional debugging:
ifdef VERBOSE
Q =
else
Q = @
endif
app: main.o
$(Q)echo "Linking..."
$(Q)gcc -o app main.o
Set Q to @ (silent) or empty (verbose). Run with make VERBOSE=1 for details.
Testing pattern rules: If pattern rules aren't matching:
make -n -p | grep "main.o"
Shows which rules could build main.o and their prerequisites. Helps diagnose why a pattern rule didn't apply.
Checking for circular dependencies:
make --trace 2>&1 | head -20
Shows the call stack. Circular dependencies often appear as loops in the trace.
Makefile syntax errors: Make's error messages can be cryptic:
Makefile:5: *** missing separator. Stop.
This means line 5 has a recipe without a tab. Check for spaces before commands.
Common mistakes:
# Wrong: space before recipe (not tab) app: main.o gcc -o app main.o # ERROR: missing separator # Wrong: variable not defined echo "$(UNDEFINED)" # Wrong: circular dependency a: b b: a # Correct: recipe with tab app: main.o gcc -o app main.o
When stuck, use make -n to see what Make thinks should happen, and make -d for detailed reasoning.
