When you include a header in a C file, GNU Make has no built-in knowledge of that relationship. If the header changes, Make will not know to recompile theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/fernandodanielmaqueda/gcc-bison-flex-GNUmakefile/llms.txt
Use this file to discover all available pages before exploring further.
.c file unless you list the header as an explicit prerequisite — something that quickly becomes impossible to maintain by hand. The framework solves this by using the compiler itself to record exactly which headers each source file depends on, then feeding those records back into Make on the next run.
What .d files are
A .d file is a fragment of valid Make syntax emitted by the C compiler. It contains a rule of the form:
obj/foo.o depends on those headers. If any header changes, Make rebuilds the object. If no .d file exists yet (first build), Make silently skips it and the object is built unconditionally, which produces the .d file as a side effect.
The DEPDIR directory
All dependency files are stored under DEPDIR, which defaults to .deps/. The directory is created automatically before any rule that needs it, as an order-only prerequisite.
The two file types in DEPDIR for each source are:
| File | Purpose |
|---|---|
$(DEPDIR)%.d | The Make-syntax dependency fragment itself |
$(DEPDIR)%.d.timestamp | A sentinel file touched after the .d file is fully written |
| Source | .d file | .d.timestamp file |
|---|---|---|
foo.c | .deps/foo.d | .deps/foo.d.timestamp |
foo.y | .deps/foo.tab.d | .deps/foo.tab.d.timestamp |
foo.l | .deps/foo.lex.yy.d | .deps/foo.lex.yy.d.timestamp |
Why timestamps are separate from .d files
The .d file is regenerated by the compiler, which writes it atomically to a .tmp.1 scratch file before being post-processed and moved into place. The .d.timestamp file is then touch-ed to mark that the whole process completed successfully.
Make uses the .d.timestamp file — not the .d file itself — as the prerequisite in build rules. This separation matters for two reasons:
- Atomicity: a crashed or interrupted compilation leaves an incomplete
.d.tmp.1rather than a corrupt.d. The.d.timestampis only updated if the full recipe succeeds. - Decoupling: the
.dfile can be re-read on the next Make invocation without that re-read itself triggering another compilation. Make tracks freshness through the timestamp file, not the content of the.dfile.
How .d files are generated: -MMD -MF
When compiling to an object file (the default GENERATE_ASSEMBLING_OUTPUT=X case), dependency generation is appended to the same compiler invocation that produces the .o:
-MMD— write dependency information for Make, excluding system headers-MF file— write the dependency output tofileinstead of stdout-MT X— use the literal stringXas the target name in the generated rule (replaced in the post-processing step)
|| { rm ... ; exit 1 ; } block removes the partial scratch file and propagates the error.
When no compilation output options are active at all (no -E, -S, or -c stop point), a separate dedicated pattern rule regenerates .d and .d.timestamp files using only the preprocessor:
-MM generates a rule without system headers, writing it to the scratch file.
The mkfwk_common_recipe_for_.d_files canned recipe
After the compiler writes the raw dependency output to $(DEPDIR)$*.d.tmp.1, the following canned recipe post-processes it:
- Write the header —
printfopens a second scratch file (.tmp.2) and writes two things into it: a declaration of theemptytarget (so that when this.dfile is included, Make’s default goal does not change), and the start of a rule for$(DEPDIR)*.d.timestampwith a continuation backslash. - Strip the placeholder target — the compiler wrote
X: ...(because of-MT X).sedreplaces thatX:on the first line with nothing, so the continuation of the timestamp rule’s prerequisite list flows straight from the header written in step 1. - Remove the first scratch file —
.tmp.1is deleted. - Atomically rename —
.tmp.2is moved to the final.dpath withmv, which is atomic on the same filesystem. - Touch the timestamp — the
.d.timestampfile is updated to signal that dependency tracking is complete and up to date.
The
empty target written at the top of every .d file is a phony target with no recipe. Its presence ensures that when Make includes the .d file as part of reading makefiles, it does not adopt the timestamp rule’s target as the default goal.Including .d files: sinclude and INCLUDE_DEPS
The framework uses sinclude (silent include) rather than include:
sinclude does not error if the listed files do not exist. On a clean build the .d files have not been generated yet, so Make silently skips them and proceeds to compile everything. After the first successful build, the .d files exist and are included on subsequent runs, providing accurate header dependency information.
Setting INCLUDE_DEPS= (empty) disables this entirely. In that case, the .d files are still listed as explicit prerequisites of the build targets (so they still get generated), but their contents are never fed back to Make, making every build a full rebuild.
The empty .d rule: preventing errors for missing targets
The .d file pattern rule has an intentionally empty recipe:
.d file and then finds that one of the listed headers no longer exists on disk, it looks for a rule to rebuild that header. Without a matching rule it would print an error. The framework adds a catch-all for header files:
.h file. Its empty recipe tells Make: “you can consider this header up to date; there is nothing to do.” Make marks the dependent objects as out of date (because the header file’s timestamp is older than the object) and recompiles them — which will either succeed (the header was moved) or fail with a proper compiler error (the header was genuinely deleted).
The
$(DEPDIR)%.d: ; rule serves a similar purpose for .d files themselves: Make may try to rebuild an included .d file if it appears out of date. The empty recipe lets Make believe it has been remade, after which the real .d.timestamp rule takes over on the next invocation.Pattern rule summary
Three pattern rules cover the three source types. All three are generated by expanding themkfwk_rule_for_.d_files template in making.mk:
.l rule additionally lists $(MKFWK_YDEFS) as a prerequisite, which expands to the set of .tab.h files produced by YACC. This ensures that token headers are present before the scanner’s generated C code is analysed for dependencies.