Skip to content

Commit

Permalink
Add multi-inputs tool
Browse files Browse the repository at this point in the history
The 'multi-inputs' option will list all <target> + <inputs> for
the given targets.

Run:
ninja -t multi-inputs <target1> <target2> <target3>

Ninja will then output:
<target1> <input_x>
<target1> <input_y>
<target2> <input_x>
<target2> <input_z>
<target3> <input_y>
  • Loading branch information
freand76 committed Nov 22, 2024
1 parent a3fda2b commit beabef0
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 0 deletions.
41 changes: 41 additions & 0 deletions doc/manual.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,47 @@ output files are out of date.
rebuild those targets.
_Available since Ninja 1.11._
`multi-inputs`:: print one or more sets of inputs required to build targets.
Each line will consist of a target, a delimiter, an input and a terminator character.
The list produced by the tool can be helpful if one would like to know which targets
that are affected by a certain input.
+
The output will be a series of lines with the following elements:
+
----
<target> <delimiter> <input> <terminator>
----
+
The default `<delimiter>` is a single TAB character.
The delimiter can be modified to any string using the `--delimiter` argument.
+
The default `<terminator>` is a line terminator (i.e. `\n` on Posix and `\r\n` on Windows).
The terminator can be changed to `\0` by using the `--print0` argument.
+
----
----
+
Example usage of the `multi-inputs` tool:
+
----
ninja -t multi-inputs target1 target2 target3
----
+
Example of produced output from the `multi-inputs` tool:
+
----
target1 file1.c
target2 file1.c
target2 file2.c
target3 file1.c
target3 file2.c
target3 file3.c
----
+
_Note that a given input may appear for several targets if it is used by more
than one targets._
_Available since Ninja 1.13._
`clean`:: remove built files. By default, it removes all built files
except for those created by the generator. Adding the `-g` flag also
removes built files created by the generator (see <<ref_rule,the rule
Expand Down
34 changes: 34 additions & 0 deletions misc/output_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,40 @@ def test_tool_compdb_targets(self) -> None:
self.assertEqual(expected, actual)


def test_tool_multi_inputs(self) -> None:
plan = '''
rule cat
command = cat $in $out
build out1 : cat in1
build out2 : cat in1 in2
build out3 : cat in1 in2 in3
'''
self.assertEqual(run(plan, flags='-t multi-inputs out1'),
'''out1<TAB>in1
'''.replace("<TAB>", "\t"))

self.assertEqual(run(plan, flags='-t multi-inputs out1 out2 out3'),
'''out1<TAB>in1
out2<TAB>in1
out2<TAB>in2
out3<TAB>in1
out3<TAB>in2
out3<TAB>in3
'''.replace("<TAB>", "\t"))

self.assertEqual(run(plan, flags='-t multi-inputs -d: out1'),
'''out1:in1
''')

self.assertEqual(
run(
plan,
flags='-t multi-inputs -d, --print0 out1 out2'
),
'''out1,in1\0out2,in1\0out2,in2\0'''
)


def test_explain_output(self):
b = BuildDir('''\
build .FORCE: phony
Expand Down
71 changes: 71 additions & 0 deletions src/ninja.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct NinjaMain : public BuildLogUser {
int ToolTargets(const Options* options, int argc, char* argv[]);
int ToolCommands(const Options* options, int argc, char* argv[]);
int ToolInputs(const Options* options, int argc, char* argv[]);
int ToolMultiInputs(const Options* options, int argc, char* argv[]);
int ToolClean(const Options* options, int argc, char* argv[]);
int ToolCleanDead(const Options* options, int argc, char* argv[]);
int ToolCompilationDatabase(const Options* options, int argc, char* argv[]);
Expand Down Expand Up @@ -845,6 +846,74 @@ int NinjaMain::ToolInputs(const Options* options, int argc, char* argv[]) {
return 0;
}

int NinjaMain::ToolMultiInputs(const Options* options, int argc, char* argv[]) {
// The inputs tool uses getopt, and expects argv[0] to contain the name of
// the tool, i.e. "inputs".
argc++;
argv--;

optind = 1;
int opt;
char terminator = '\n';
const char* delimiter = "\t";
const option kLongOptions[] = { { "help", no_argument, NULL, 'h' },
{ "delimiter", required_argument, NULL,
'd' },
{ "print0", no_argument, NULL, '0' },
{ NULL, 0, NULL, 0 } };
while ((opt = getopt_long(argc, argv, "d:h0", kLongOptions, NULL)) != -1) {
switch (opt) {
case 'd':
delimiter = optarg;
break;
case '0':
terminator = '\0';
break;
case 'h':
default:
// clang-format off
printf(
"Usage '-t multi-inputs [options] [targets]\n"
"\n"
"Print one or more sets of inputs required to build targets, sorted in dependency order.\n"
"The tool works like inputs tool but with addition of the target for each line.\n"
"The output will be a series of lines with the following elements:\n"
"<target> <delimiter> <input> <terminator>\n"
"Note that a given input may appear for several targets if it is used by more than one targets.\n"
"Options:\n"
" -h, --help Print this message.\n"
" -d --delimiter=DELIM Use DELIM instead of TAB for field delimiter.\n"
" -0, --print0 Use \\0, instead of \\n as a line terminator.\n"
);
// clang-format on
return 1;
}
}
argv += optind;
argc -= optind;

std::vector<Node*> nodes;
std::string err;
if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
Error("%s", err.c_str());
return 1;
}

for (const Node* node : nodes) {
InputsCollector collector;

collector.VisitNode(node);
std::vector<std::string> inputs = collector.GetInputsAsStrings();

for (const std::string& input : inputs) {
printf("%s%s%s", node->path().c_str(), delimiter, input.c_str());
fputc(terminator, stdout);
}
}

return 0;
}

int NinjaMain::ToolClean(const Options* options, int argc, char* argv[]) {
// The clean tool uses getopt, and expects argv[0] to contain the name of
// the tool, i.e. "clean".
Expand Down Expand Up @@ -1234,6 +1303,8 @@ const Tool* ChooseTool(const string& tool_name) {
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands },
{ "inputs", "list all inputs required to rebuild given targets",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolInputs},
{ "multi-inputs", "print one or more sets of inputs required to build targets",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolMultiInputs},
{ "deps", "show dependencies stored in the deps log",
Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps },
{ "missingdeps", "check deps log dependencies on generated files",
Expand Down

0 comments on commit beabef0

Please sign in to comment.