From a4970769519b09fec5ff6ffe73a5fa2bf9f252e4 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 1 Oct 2015 15:24:58 -0400 Subject: Add a "dyndep" reserved binding to the manifest format Allow rules or build statements to specify one of the build statement inputs in a "dyndep" binding. This will later be used to load dependency information from the specified file. --- src/eval_env.cc | 1 + src/graph.cc | 5 ++++ src/graph.h | 18 ++++++++++-- src/manifest_parser.cc | 17 +++++++++++ src/manifest_parser_test.cc | 70 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 3 deletions(-) diff --git a/src/eval_env.cc b/src/eval_env.cc index 8817a87..aa3d2b6 100644 --- a/src/eval_env.cc +++ b/src/eval_env.cc @@ -65,6 +65,7 @@ const EvalString* Rule::GetBinding(const string& key) const { bool Rule::IsReservedBinding(const string& var) { return var == "command" || var == "depfile" || + var == "dyndep" || var == "description" || var == "deps" || var == "generator" || diff --git a/src/graph.cc b/src/graph.cc index bf9363d..2fbce84 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -387,6 +387,11 @@ string Edge::GetUnescapedDepfile() { return env.LookupVariable("depfile"); } +string Edge::GetUnescapedDyndep() { + EdgeEnv env(this, EdgeEnv::kDoNotEscape); + return env.LookupVariable("dyndep"); +} + string Edge::GetUnescapedRspfile() { EdgeEnv env(this, EdgeEnv::kDoNotEscape); return env.LookupVariable("rspfile"); diff --git a/src/graph.h b/src/graph.h index 20af578..745297d 100644 --- a/src/graph.h +++ b/src/graph.h @@ -40,6 +40,7 @@ struct Node { slash_bits_(slash_bits), mtime_(-1), dirty_(false), + dyndep_pending_(false), in_edge_(NULL), id_(-1) {} @@ -87,6 +88,9 @@ struct Node { void set_dirty(bool dirty) { dirty_ = dirty; } void MarkDirty() { dirty_ = true; } + bool dyndep_pending() const { return dyndep_pending_; } + void set_dyndep_pending(bool pending) { dyndep_pending_ = pending; } + Edge* in_edge() const { return in_edge_; } void set_in_edge(Edge* edge) { in_edge_ = edge; } @@ -116,6 +120,10 @@ private: /// edges to build. bool dirty_; + /// Store whether dyndep information is expected from this node but + /// has not yet been loaded. + bool dyndep_pending_; + /// The Edge that produces this Node, or NULL when there is no /// known edge to produce it. Edge* in_edge_; @@ -135,9 +143,10 @@ struct Edge { VisitDone }; - Edge() : rule_(NULL), pool_(NULL), env_(NULL), mark_(VisitNone), - outputs_ready_(false), deps_loaded_(false), deps_missing_(false), - implicit_deps_(0), order_only_deps_(0), implicit_outs_(0) {} + Edge() : rule_(NULL), pool_(NULL), dyndep_(NULL), env_(NULL), + mark_(VisitNone), outputs_ready_(false), deps_loaded_(false), + deps_missing_(false), implicit_deps_(0), order_only_deps_(0), + implicit_outs_(0) {} /// Return true if all inputs' in-edges are ready. bool AllInputsReady() const; @@ -153,6 +162,8 @@ struct Edge { /// Like GetBinding("depfile"), but without shell escaping. string GetUnescapedDepfile(); + /// Like GetBinding("dyndep"), but without shell escaping. + string GetUnescapedDyndep(); /// Like GetBinding("rspfile"), but without shell escaping. string GetUnescapedRspfile(); @@ -162,6 +173,7 @@ struct Edge { Pool* pool_; vector inputs_; vector outputs_; + Node* dyndep_; BindingEnv* env_; VisitMark mark_; bool outputs_ready_; diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 226acb0..2011368 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -387,6 +387,23 @@ bool ManifestParser::ParseEdge(string* err) { err); } + // Lookup, validate, and save any dyndep binding. It will be used later + // to load generated dependency information dynamically, but it must + // be one of our manifest-specified inputs. + string dyndep = edge->GetUnescapedDyndep(); + if (!dyndep.empty()) { + uint64_t slash_bits; + if (!CanonicalizePath(&dyndep, &slash_bits, err)) + return false; + edge->dyndep_ = state_->GetNode(dyndep, slash_bits); + edge->dyndep_->set_dyndep_pending(true); + vector::iterator dgi = + std::find(edge->inputs_.begin(), edge->inputs_.end(), edge->dyndep_); + if (dgi == edge->inputs_.end()) { + return lexer_.Error("dyndep '" + dyndep + "' is not an input", err); + } + } + return true; } diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index c91d8d1..f2b7467 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -1085,3 +1085,73 @@ TEST_F(ParserTest, CRLF) { " description = YAY!\r\n", &err)); } + +TEST_F(ParserTest, DyndepNotSpecified) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build result: cat in\n")); + Edge* edge = state.GetNode("result", 0)->in_edge(); + ASSERT_FALSE(edge->dyndep_); +} + +TEST_F(ParserTest, DyndepNotInput) { + State lstate; + ManifestParser parser(&lstate, NULL); + string err; + EXPECT_FALSE(parser.ParseTest( +"rule touch\n" +" command = touch $out\n" +"build result: touch\n" +" dyndep = notin\n", + &err)); + EXPECT_EQ("input:5: dyndep 'notin' is not an input\n", err); +} + +TEST_F(ParserTest, DyndepExplicitInput) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build result: cat in\n" +" dyndep = in\n")); + Edge* edge = state.GetNode("result", 0)->in_edge(); + ASSERT_TRUE(edge->dyndep_); + EXPECT_TRUE(edge->dyndep_->dyndep_pending()); + EXPECT_EQ(edge->dyndep_->path(), "in"); +} + +TEST_F(ParserTest, DyndepImplicitInput) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build result: cat in | dd\n" +" dyndep = dd\n")); + Edge* edge = state.GetNode("result", 0)->in_edge(); + ASSERT_TRUE(edge->dyndep_); + EXPECT_TRUE(edge->dyndep_->dyndep_pending()); + EXPECT_EQ(edge->dyndep_->path(), "dd"); +} + +TEST_F(ParserTest, DyndepOrderOnlyInput) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build result: cat in || dd\n" +" dyndep = dd\n")); + Edge* edge = state.GetNode("result", 0)->in_edge(); + ASSERT_TRUE(edge->dyndep_); + EXPECT_TRUE(edge->dyndep_->dyndep_pending()); + EXPECT_EQ(edge->dyndep_->path(), "dd"); +} + +TEST_F(ParserTest, DyndepRuleInput) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +" dyndep = $in\n" +"build result: cat in\n")); + Edge* edge = state.GetNode("result", 0)->in_edge(); + ASSERT_TRUE(edge->dyndep_); + EXPECT_TRUE(edge->dyndep_->dyndep_pending()); + EXPECT_EQ(edge->dyndep_->path(), "in"); +} -- cgit v1.2.1