Forensic Debugging with Make Evidence¶
Page Maps¶
graph LR
family["Reproducible Research"]
program["Deep Dive Make"]
section["Determinism Debugging Self Testing"]
page["Forensic Debugging with Make Evidence"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
orient["Orient on the page map"] --> read["Read the main claim and examples"]
read --> inspect["Inspect the related code, proof, or capstone surface"]
inspect --> verify["Run or review the verification path"]
verify --> apply["Apply the idea back to the module and capstone"]
Make debugging gets calmer when you stop asking the build system to reveal its soul and start asking it for concrete evidence.
The debugging ladder¶
Use these in order:
make -n <target>make --trace <target>make -p- only then add temporary probes such as
$(info ...)or$(warning ...)
That order matters because it keeps the explanation grounded in the evaluated graph.
Why the order matters¶
It is common to jump straight to adding echo lines or rewriting a recipe. That tends to
mix symptoms with causes. The ladder above forces a cleaner sequence:
- inspect the intended work
- inspect the reason for the work
- inspect the evaluated world
- only then add temporary probes if something is still hidden
That keeps debugging factual instead of theatrical.
What each command is for¶
-nanswers what would run--traceanswers why Make thinks it must run-panswers what rules and variables Make actually evaluated
The most important one is usually --trace. If you cannot quote the line that forced the
rebuild, you usually have not located the cause yet.
A small example of the right question¶
Wrong question:
- "Why is Make acting strangely?"
Better question:
- "Which prerequisite or target state made
build/main.oout of date according to--trace?"
That sounds smaller, but it is much more actionable. The smaller question leads you back to a specific edge or artifact instead of an opinion about the whole build.
What make -p is for in this module¶
make -p is where you go when the source file and the evaluated build no longer seem to
match. It helps answer questions like:
- what did this variable expand to
- which rules actually exist after includes and conditionals
- whether the build used the value you think it used
That is especially important in Module 03 because hidden variability and optional abstraction can move behavior away from what a quick read suggests.
What this lesson is protecting you from¶
Common bad debugging habits include:
- saying "it rebuilt for no reason"
- editing recipes before confirming the triggering edge
- assuming a variable has one value because the source file "looks like" it should
This lesson trains you to use the build’s own evidence first.
A good debugging sentence¶
Try to make yourself say the issue this way:
"build/include/dynamic.h rebuilt because scripts/gen_dynamic_h.py was newer than the
target according to --trace."
That is a debug statement. It names the target, the cause, and the evidence.
End-of-page checkpoint¶
Before leaving this page, you should be able to explain:
- why
--traceis usually the first real explanation tool - when
make -pbecomes more useful than rereading the source - why temporary probes come last, not first
- how to phrase a rebuild explanation as evidence instead of opinion