diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build_test.cc | 16 | ||||
-rw-r--r-- | src/graph.cc | 3 | ||||
-rw-r--r-- | src/graph.h | 12 | ||||
-rw-r--r-- | src/graph_test.cc | 44 | ||||
-rw-r--r-- | src/manifest_parser.cc | 15 | ||||
-rw-r--r-- | src/manifest_parser_test.cc | 34 |
6 files changed, 122 insertions, 2 deletions
diff --git a/src/build_test.cc b/src/build_test.cc index 20fb664..7c6060d 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -717,6 +717,22 @@ TEST_F(BuildTest, TwoOutputs) { EXPECT_EQ("touch out1 out2", command_runner_.commands_ran_[0]); } +TEST_F(BuildTest, ImplicitOutput) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch $out $out.imp\n" +"build out | out.imp: touch in.txt\n")); + fs_.Create("in.txt", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out.imp", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + EXPECT_EQ("touch out out.imp", command_runner_.commands_ran_[0]); +} + // Test case from // https://github.com/ninja-build/ninja/issues/148 TEST_F(BuildTest, MultiOutIn) { diff --git a/src/graph.cc b/src/graph.cc index 98f1461..98a2c18 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -241,8 +241,9 @@ string EdgeEnv::LookupVariable(const string& var) { edge_->inputs_.begin() + explicit_deps_count, var == "in" ? ' ' : '\n'); } else if (var == "out") { + int explicit_outs_count = edge_->outputs_.size() - edge_->implicit_outs_; return MakePathList(edge_->outputs_.begin(), - edge_->outputs_.end(), + edge_->outputs_.begin() + explicit_outs_count, ' '); } diff --git a/src/graph.h b/src/graph.h index cf15123..add8d3d 100644 --- a/src/graph.h +++ b/src/graph.h @@ -129,7 +129,7 @@ private: struct Edge { Edge() : rule_(NULL), pool_(NULL), env_(NULL), outputs_ready_(false), deps_missing_(false), - implicit_deps_(0), order_only_deps_(0) {} + implicit_deps_(0), order_only_deps_(0), implicit_outs_(0) {} /// Return true if all inputs' in-edges are ready. bool AllInputsReady() const; @@ -181,6 +181,16 @@ struct Edge { return index >= inputs_.size() - order_only_deps_; } + // There are two types of outputs. + // 1) explicit outs, which show up as $out on the command line; + // 2) implicit outs, which the target generates but are not part of $out. + // These are stored in outputs_ in that order, and we keep a count of + // #2 to use when we need to access the various subsets. + int implicit_outs_; + bool is_implicit_out(size_t index) const { + return index >= outputs_.size() - implicit_outs_; + } + bool is_phony() const; bool use_console() const; }; diff --git a/src/graph_test.cc b/src/graph_test.cc index 44be8a5..723e8ea 100644 --- a/src/graph_test.cc +++ b/src/graph_test.cc @@ -105,6 +105,50 @@ TEST_F(GraphTest, ExplicitImplicit) { EXPECT_TRUE(GetNode("out.o")->dirty()); } +TEST_F(GraphTest, ImplicitOutputParse) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + + Edge* edge = GetNode("out")->in_edge(); + EXPECT_EQ(2, edge->outputs_.size()); + EXPECT_EQ("out", edge->outputs_[0]->path()); + EXPECT_EQ("out.imp", edge->outputs_[1]->path()); + EXPECT_EQ(1, edge->implicit_outs_); + EXPECT_EQ(edge, GetNode("out.imp")->in_edge()); +} + +TEST_F(GraphTest, ImplicitOutputMissing) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + fs_.Create("in", ""); + fs_.Create("out", ""); + + Edge* edge = GetNode("out")->in_edge(); + string err; + EXPECT_TRUE(scan_.RecomputeDirty(edge, &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out")->dirty()); + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + +TEST_F(GraphTest, ImplicitOutputOutOfDate) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + fs_.Create("out.imp", ""); + fs_.Tick(); + fs_.Create("in", ""); + fs_.Create("out", ""); + + Edge* edge = GetNode("out")->in_edge(); + string err; + EXPECT_TRUE(scan_.RecomputeDirty(edge, &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out")->dirty()); + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + TEST_F(GraphTest, PathWithCurrentDirectory) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "rule catdep\n" diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 8bdb330..be267a3 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -248,6 +248,20 @@ bool ManifestParser::ParseEdge(string* err) { } while (!out.empty()); } + // Add all implicit outs, counting how many as we go. + int implicit_outs = 0; + if (lexer_.PeekToken(Lexer::PIPE)) { + for (;;) { + EvalString out; + if (!lexer_.ReadPath(&out, err)) + return err; + if (out.empty()) + break; + outs.push_back(out); + ++implicit_outs; + } + } + if (!ExpectToken(Lexer::COLON, err)) return false; @@ -351,6 +365,7 @@ bool ManifestParser::ParseEdge(string* err) { delete edge; return true; } + edge->implicit_outs_ = implicit_outs; edge->inputs_.reserve(ins.size()); for (vector<EvalString>::iterator i = ins.begin(); i != ins.end(); ++i) { diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index cb9f405..e8c1907 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -927,6 +927,40 @@ TEST_F(ParserTest, OrderOnly) { ASSERT_TRUE(edge->is_order_only(1)); } +TEST_F(ParserTest, ImplicitOutput) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo | imp: cat bar\n")); + + Edge* edge = state.LookupNode("imp")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 2); + EXPECT_TRUE(edge->is_implicit_out(1)); +} + +TEST_F(ParserTest, ImplicitOutputEmpty) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo | : cat bar\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 1); + EXPECT_FALSE(edge->is_implicit_out(0)); +} + +TEST_F(ParserTest, NoExplicitOutput) { + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + string err; + EXPECT_FALSE(parser.ParseTest( +"rule cat\n" +" command = cat $in > $out\n" +"build | imp : cat bar\n", &err)); + ASSERT_EQ("input:3: expected path\n" + "build | imp : cat bar\n" + " ^ near here", err); +} + TEST_F(ParserTest, DefaultDefault) { ASSERT_NO_FATAL_FAILURE(AssertParse( "rule cat\n command = cat $in > $out\n" |