Skip to content

Conversation

moritzx22
Copy link

the command
ninja -t graph mytarget
will generate a .dot file to visualize the dependency graph. This is a good working feature to understand the build.
But for large (cmake) projects, the generated .dot file is getting large as well. Generating a graphic file from those large files is practically not possible.

For the case you only want to see a subset of the graph of a dedicated target, I propose to add the following option:
ninja -t graph -d 1 mytarget
ninja -t graph -d [integer] mytarget
The -d option gives a possibility to specify the maximum dependency distance of the target and their inputs.
Without the -d flag, the behavior is as before, same as for negative values.

In the following, some examples using the ninja as an target

ninja -t graph -d 0 ninja
depth0
ninja -t graph -d 1 ninja
depth1
ninja -t graph ninja
original

@moritzx22
Copy link
Author

the command line
ninja -t graph --help
prints the following output

Usage '-t graph [options] [targets]

output graphviz dot file for targets.
For smaller outputs try the --depth option.
  example:
   ninja -t graph mytarget | dot -Tsvg -ograph.svg
Options:
  -h, --help   Print this message.
  -d, --depth  limit the dependency depth to inputs.

for (vector<Node*>::iterator in = edge->inputs_.begin();
in != edge->inputs_.end(); ++in) {
AddTarget(*in, depth < 0 ? -1 : depth - 1);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity, I suggest using if (depth > 0) { ... } here and just call AddTarget(..., depth - 1).

Also, we are now supporting C++14 so feel free to use range-based loops as in:

if (depth > 0) {
  for (const Node* input : edge->inputs_)
    AddTarget(input, depth - 1);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, this also means setting depth = INT_MAX by default.

Copy link
Author

@moritzx22 moritzx22 Aug 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarification cli

  • ninja -t graph ninja
    • depth is unlimited by default
  • ninja -t graph -d 0 ninja
    • depth is limited
    • function GraphViz::AddTarget is executed one time
    • recursion depth is set to 0
  • ninja -t graph -d 1 ninja
    • depth is limited
    • function GraphViz::AddTarget is executed several times
    • recursion depth is set to 1

see for an example: #2594 (comment)

Copy link
Author

@moritzx22 moritzx22 Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed depth to be positive only. 590c905

src/graphviz.cc Outdated
string pathstr = node->path();
replace(pathstr.begin(), pathstr.end(), '\\', '/');
printf("\"%p\" [label=\"%s\"]\n", node, pathstr.c_str());
labeled_nodes_.insert(node);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why is this using labeled_nodes_ here while the original code that now calls this did not? It looks like this would change the output considerably if -d is not used.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent question. My first approach was not using the labeled_nodes_, the effect was that some of the nodes were displayed with their hashes, not with names.

This happened due to early interruption of the recursion, some of the "print labeling" did miss. This never happened in the original algorithm, as there was no filtering.

labeled_nodes_ shall make sure the label is only printed once. The new algorithm is doing the labeling immediately a node/linkage is printed. The original algorithm could make the labeling in a next recursion for nodes that has already been printed. This is fine as long as the recursion does not end early...

if -d is not used, there is (or shall) no semantic change in the output. Just the order of the single dot commands might change...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note:

I think that the current algorithm could be split in two part to be more clean and future proof:

  • Filtering
    • result -> std::set<const Node*>
  • generating the output
    • print std::set<const Node*>

Right now the algorithm does that all in one step. I did this because code changes are much smaller and it is easier to track changes for the reviewer. The current filter option is easy to implement with current design of the algorithm.

I could think of other filter options, here some ideas:

  • exclude targets
    • ninja -t graph -x NegativeRegexPattern myTarget
    • would print my target excluding target matching NegativeRegexPattern
  • exclude link types
    • ninja -t graph -xOrderOnly myTarget
    • do not print order only links
  • `ninja -t graph -dependency MyTarget1..myTarget2..myTarget3
    • would print all target that are in between the linkage path of all target specified
    • this would cover the case if the user wants to see the relationship of 2 or more targets, but is not interested in the remaining targets
    • this would be a more complicated filter and most likely breaking the current design of the algorithm

There might be more useful filter options I do not have in mind yet. But before making bigger steps and invest more effort, there should be a decision if the feature "filter option for graph" should be part of the ninja-build.

…g the graph is limited to a given value. For large projects, the generated dot file is large as well and can not be used to generate a graphic. With this new option the size can be limited.
Add const correctness.
foor loop C++14.

add missing labelings.
@moritzx22 moritzx22 force-pushed the graph branch 2 times, most recently from 2162b24 to eb4a526 Compare August 22, 2025 19:28
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