Skip to content

Commit

Permalink
Make the output of ninja -t inputs deterministic
Browse files Browse the repository at this point in the history
This sorts the output of `ninja -t inputs` to make it
deterministic and remove duplicates, and adds a regression
test in output_test.py
  • Loading branch information
digit-android authored and digit-google committed Feb 8, 2022
1 parent f404f00 commit e2708f4
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 7 deletions.
15 changes: 15 additions & 0 deletions misc/output_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,20 @@ def test_entering_directory_on_stdout(self):
output = run(Output.BUILD_SIMPLE_ECHO, flags='-C$PWD', pipe=True)
self.assertEqual(output.splitlines()[0][:25], "ninja: Entering directory")

def test_tool_inputs(self):
self.assertEqual(run(
'''
rule cat
command = cat $in $out
build out1 : cat in1
build out2 : cat in2 out1
build out3 : cat out2 out1 | implicit || order_only
''', '-t inputs out3'),
'''in1
in2
out1
out2
''')

if __name__ == '__main__':
unittest.main()
22 changes: 22 additions & 0 deletions src/graph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,28 @@ std::string EdgeEnv::MakePathList(const Node* const* const span,
return result;
}

void Edge::CollectInputs(bool shell_escape,
std::vector<std::string>* out) const {
int count = inputs_.size() - implicit_deps_ - order_only_deps_;
for (int n = 0; n < count; ++n) {
std::string path = inputs_[n]->PathDecanonicalized();
if (shell_escape) {
std::string unescaped;
unescaped.swap(path);
#ifdef _WIN32
GetWin32EscapedString(unescaped, &path);
#else
GetShellEscapedString(unescaped, &path);
#endif
}
#if __cplusplus >= 201103L
out->push_back(std::move(path));
#else
out->push_back(path);
#endif
}
}

std::string Edge::EvaluateCommand(const bool incl_rsp_file) const {
string command = GetBinding("command");
if (incl_rsp_file) {
Expand Down
3 changes: 3 additions & 0 deletions src/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ struct Edge {

void Dump(const char* prefix="") const;

// Append all edge explicit inputs to |*out|. Possibly with shell escaping.
void CollectInputs(bool shell_escape, std::vector<std::string>* out) const;

const Rule* rule_;
Pool* pool_;
std::vector<Node*> inputs_;
Expand Down
25 changes: 25 additions & 0 deletions src/graph_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,31 @@ TEST_F(GraphTest, RootNodes) {
}
}

TEST_F(GraphTest, CollectInputs) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
&state_,
"build out$ 1: cat in1 in2 in$ with$ space | implicit || order_only\n"));

std::vector<std::string> inputs;
Edge* edge = GetNode("out 1")->in_edge();
edge->CollectInputs(false, &inputs);
EXPECT_EQ(3u, inputs.size());
EXPECT_EQ("in1", inputs[0]);
EXPECT_EQ("in2", inputs[1]);
EXPECT_EQ("in with space", inputs[2]);

inputs.clear();
edge->CollectInputs(true, &inputs);
EXPECT_EQ(3u, inputs.size());
EXPECT_EQ("in1", inputs[0]);
EXPECT_EQ("in2", inputs[1]);
#ifdef _WIN32
EXPECT_EQ("\"in with space\"", inputs[2]);
#else
EXPECT_EQ("'in with space'", inputs[2]);
#endif
}

TEST_F(GraphTest, VarInOutPathEscaping) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build a$ b: cat no'space with$ space$$ no\"space2\n"));
Expand Down
26 changes: 19 additions & 7 deletions src/ninja.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
#include <unistd.h>
#endif

#include <algorithm>

#include "browse.h"
#include "build.h"
#include "build_log.h"
#include "deps_log.h"
#include "clean.h"
#include "debug_flags.h"
#include "depfile_parser.h"
#include "deps_log.h"
#include "disk_interface.h"
#include "graph.h"
#include "graphviz.h"
Expand Down Expand Up @@ -744,18 +746,20 @@ int NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) {
return 0;
}

void PrintInputs(Edge* edge, set<Edge*>* seen) {
void CollectInputs(Edge* edge, set<Edge*>* seen,
std::vector<std::string>* result) {
if (!edge)
return;
if (!seen->insert(edge).second)
return;

for (vector<Node*>::iterator in = edge->inputs_.begin();
in != edge->inputs_.end(); ++in)
PrintInputs((*in)->in_edge(), seen);
CollectInputs((*in)->in_edge(), seen, result);

if (!edge->is_phony())
puts(edge->GetBinding("in_newline").c_str());
if (!edge->is_phony()) {
edge->CollectInputs(true, result);
}
}

int NinjaMain::ToolInputs(const Options* options, int argc, char* argv[]) {
Expand All @@ -766,9 +770,17 @@ int NinjaMain::ToolInputs(const Options* options, int argc, char* argv[]) {
return 1;
}

set<Edge*> seen;
std::set<Edge*> seen;
std::vector<std::string> result;
for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in)
PrintInputs((*in)->in_edge(), &seen);
CollectInputs((*in)->in_edge(), &seen, &result);

// Make output deterministic by sorting then removing duplicates.
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());

for (size_t n = 0; n < result.size(); ++n)
puts(result[n].c_str());

return 0;
}
Expand Down

0 comments on commit e2708f4

Please sign in to comment.