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");
+}