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.
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 @@
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 @@
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 @@
slash_bits_(slash_bits),
mtime_(-1),
dirty_(false),
+ dyndep_pending_(false),
in_edge_(NULL),
id_(-1) {}
@@ -87,6 +88,9 @@
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 @@
/// 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 @@
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 @@
/// 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 @@
Pool* pool_;
vector<Node*> inputs_;
vector<Node*> 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 @@
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<Node*>::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 @@
" 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");
+}