Skip to content

Missingdep and dynamic dependencies#2745

Open
moritzx22 wants to merge 8 commits intoninja-build:masterfrom
moritzx22:missingdep_dyndep
Open

Missingdep and dynamic dependencies#2745
moritzx22 wants to merge 8 commits intoninja-build:masterfrom
moritzx22:missingdep_dyndep

Conversation

@moritzx22
Copy link
Copy Markdown
Contributor

Extend 'ninja -t missingdeps' to Support Dyndeps

Currently, the ninja -t missingdeps subtool does not take dynamic dependencies (dyndep) into account. This PR extends the functionality of missingdeps to properly follow and evaluate dyndep relationships.

The example below demonstrates such a case:
A missing dependency is reported even though it is correctly satisfied through a dynamically generated input generated.h.

Show full `build.ninja` file
rule catdep
  depfile = a.d
  dyndep = dyndep.dd
  command = cat $in > $out && printf 'a.o: generated.h\n' > a.d
build a.o: catdep a.c || dyndep.dd

rule touch
  command = touch $out
build generated.h X: touch in

rule dd
  command = touch dyndep.dd && printf 'ninja_dyndep_version = 1\nbuild a.o: dyndep | X\n' > $out
build dyndep.dd: dd

a

As of today, running ninja -t missingdeps on the first example produces:

$ ninja -t missingdeps
Missing dep: a.o uses generated.h (generated by touch)
Processed 4 nodes.
Error: There are 1 missing dependency paths.
1 targets had depfile dependencies on 1 distinct generated inputs (from 1 rules)  without a non-depfile dep path to the generator.
There might be build flakiness if any of the targets listed above are built alone, or not late enough, in a clean output directory.

This is a false positive, since generated.h is actually provided via a dyndep relationship. However, because dyndeps are currently ignored by the missingdeps tool, the dependency path is not detected.

Changes in This PR

Extend the missingdeps algorithm to:

  • Traverse and evaluate dynamic dependencies (dyndep)
  • Incorporate dynamically discovered inputs into dependency analysis
  • Eliminate false positives caused by dependencies that are only known after dyndep evaluation
    Running the same example with the proposed
$ ninja -t missingdeps
Processed 4 nodes.
No missing dependencies on generated files found.

The false positive is resolved, as the tool now correctly follows dyndep relationships and recognizes that generated.h is properly generated before it is consumed.

Missingdyndeps

This PR also introduces the concept of "missingdyndeps", analogous to missingdep from depfiles.

A missingdyndep occurs when:

  • A build edge depends on dynamically generated outputs,
  • But the dyndep-producing edge is not guaranteed to run before the dependent edge.

In a well-defined build:

  • All dynamic outputs must be generated before they are consumed,
  • Even if the corresponding dyndep file has not yet been loaded.

Example missingdyndep:

The following example demonstrates a missing dyndep situation:

Show full `build.ninja` file
rule dd
  command = touch dyndep.dd && echo 'ninja_dyndep_version = 1\nbuild out | generated.h: dyndep\n' > dyndep.dd

rule dyn_touch
  command = touch generated.h out
  dyndep = dyndep.dd

rule request
  command = cat $in > $out

build dyndep.dd: dd
build out: dyn_touch || dyndep.dd
build b.out: request generated.h

b

In this setup, it is not guaranteed that the dyn_touch edge runs before the request edge if the dyndep file has not yet been loaded. As a result, generated.h may be consumed before it is produced.

The detection of missingdyndeps is integrated into the existing missingdeps tool. When such a case is encountered, it is reported alongside the usual output.

For the example above, the output becomes:

$ ninja -t missingdeps
Processed 4 nodes.
No missing dep found
Missing Dyndep: 'generated.h' is an input to 'b.out' and a dyndep-output of 'dyndep.dd'
There might be build flakiness if any of the targets listed above are built alone, or not late enough, in a clean output directory.

This indicates that while no traditional missingdeps were found, a missing dyndep condition was still detected.

Resolve example

This can be resolved by explicitly enforcing the correct build order, for example:

...
#build b.out: request generated.h

#Option 1:
build b.out: request generated.h || out

#Option 2:
#build b.out: request generated.h || dyndep.dd

Both approaches ensure that the dynamic dependency is properly respected and that generated.h is generated before it is used.

Here are the graphs without missingdyndeps

graph click to show

Option 1:
b2
Option 2:
b3

…file hook

This change restructures depfile loading by introducing a const‑aware function helper
and removing the virtual ProcessDepfileDeps path.
NodeStoringImplicitDepLoader is updated accordingly.
Behavior is unchanged; this prepares the codebase for future support of const 'Edge*'
in dependency scanning.
@moritzx22 moritzx22 force-pushed the missingdep_dyndep branch 5 times, most recently from 67ff575 to 2d10da7 Compare March 19, 2026 18:01
@jhasse
Copy link
Copy Markdown
Collaborator

jhasse commented Mar 19, 2026

Ignore the codespell stuff ... I'm not sure if that shouldn't just be removed from CI.

Prepare the missingdeps scanner for handling dependencies generated
through dyndep files. This change loads dyndep information into a
separate structure, making dyndep-generated inputs accessible to the
scanner without modifying the existing dependency graph.

No functional behavior is changed in this commit. This refactoring lays
the groundwork for a follow-up change that will extend missingdeps to
detect dyndep-generated inputs and outputs.
The 'ninja -t missingdeps' tool previously detected missing dependencies
only for implicit inputs generated directly by the build manifest.
Inputs generated through dyndep files were not considered,
causing missing dependencies to go unnoticed.

This commit extends the missingdeps scanner to also detect dyndep‑generated outputs,
which correspond to the dependencies listed in the depfiles.
missingdeps now follows input dependencies introduced by dyndep files,
not only those declared in the manifest. This ensures that dependency
scanning correctly determines whether a path exists between two edges,
including dynamically generated inputs.

With this change, missing dependencies can now be fixed using dyndep-generated inputs.
… edge

Nodes created via dyndep had no in_edge, causing missing‑deps checks to be skipped or incorrect.
This patch resolves the dyndep in_edge, recurses into dyndep inputs, and passes the correct edge into ProcessNodeDeps.
This change introduces a second dependency‑consistency check, analogous
to the existing 'missingdeps' mechanism, but applied to nodes generated
through dyndep. The scanner now verifies that every dyndep output required
by a requesting edge is guaranteed to be built before that edge executes,
even in cases where the dyndep file has not yet been loaded.

This ensures that incomplete or incorrect propagation of dyndep‑generated
dependencies is detected early.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants