From 13dd08c1a03e5a8f4299816fbd3af1b6cb6d9642 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sat, 27 Oct 2012 13:09:40 -0700
Subject: rearrange handling of builtin bindings to make rules simpler

Now, a 'build' block can override any special binding like 'command'
or 'description' if it needs to.
---
 src/build.cc                | 38 +++++++++++----------
 src/clean.cc                |  6 ++--
 src/eval_env.cc             | 16 +++++++++
 src/eval_env.h              | 12 +++++++
 src/graph.cc                | 81 +++++++++++++++++++++++++--------------------
 src/graph.h                 | 43 ++++++------------------
 src/graph_test.cc           | 35 ++++++++++++++++++++
 src/manifest_parser.cc      | 76 ++++++++++++++----------------------------
 src/manifest_parser_test.cc | 20 ++++++-----
 src/state.cc                |  4 +--
 src/state.h                 |  2 +-
 src/state_test.cc           |  4 +--
 12 files changed, 183 insertions(+), 154 deletions(-)

diff --git a/src/build.cc b/src/build.cc
index b4229c4..701fa92 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -256,9 +256,9 @@ void BuildStatus::PrintStatus(Edge* edge) {
 
   bool force_full_command = config_.verbosity == BuildConfig::VERBOSE;
 
-  string to_print = edge->GetDescription();
+  string to_print = edge->GetBinding("description");
   if (to_print.empty() || force_full_command)
-    to_print = edge->EvaluateCommand();
+    to_print = edge->GetBinding("command");
 
 #ifdef _WIN32
   CONSOLE_SCREEN_BUFFER_INFO csbi;
@@ -612,7 +612,7 @@ void Builder::Cleanup() {
 
     for (vector<Edge*>::iterator i = active_edges.begin();
          i != active_edges.end(); ++i) {
-      bool has_depfile = !(*i)->rule_->depfile().empty();
+      string depfile = (*i)->GetBinding("depfile");
       for (vector<Node*>::iterator ni = (*i)->outputs_.begin();
            ni != (*i)->outputs_.end(); ++ni) {
         // Only delete this output if it was actually modified.  This is
@@ -622,12 +622,13 @@ void Builder::Cleanup() {
         // need to rebuild an output because of a modified header file
         // mentioned in a depfile, and the command touches its depfile
         // but is interrupted before it touches its output file.)
-        if (has_depfile ||
-            (*ni)->mtime() != disk_interface_->Stat((*ni)->path()))
+        if (!depfile.empty() ||
+            (*ni)->mtime() != disk_interface_->Stat((*ni)->path())) {
           disk_interface_->RemoveFile((*ni)->path());
+        }
       }
-      if (has_depfile)
-        disk_interface_->RemoveFile((*i)->EvaluateDepFile());
+      if (!depfile.empty())
+        disk_interface_->RemoveFile(depfile);
     }
   }
 }
@@ -771,11 +772,11 @@ bool Builder::StartEdge(Edge* edge, string* err) {
 
   // Create response file, if needed
   // XXX: this may also block; do we care?
-  if (edge->HasRspFile()) {
-    if (!disk_interface_->WriteFile(edge->GetRspFile(),
-                                    edge->GetRspFileContent())) {
+  string rspfile = edge->GetBinding("rspfile");
+  if (!rspfile.empty()) {
+    string content = edge->GetBinding("rspfile_content");
+    if (!disk_interface_->WriteFile(rspfile, content))
       return false;
-    }
   }
 
   // start command computing and run it
@@ -792,7 +793,7 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
   TimeStamp restat_mtime = 0;
 
   if (success) {
-    if (edge->rule().restat() && !config_.dry_run) {
+    if (edge->GetBindingBool("restat") && !config_.dry_run) {
       bool node_cleaned = false;
 
       for (vector<Node*>::iterator i = edge->outputs_.begin();
@@ -817,9 +818,9 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
             restat_mtime = input_mtime;
         }
 
-        if (restat_mtime != 0 && !edge->rule().depfile().empty()) {
-          TimeStamp depfile_mtime =
-              disk_interface_->Stat(edge->EvaluateDepFile());
+        string depfile = edge->GetBinding("depfile");
+        if (restat_mtime != 0 && !depfile.empty()) {
+          TimeStamp depfile_mtime = disk_interface_->Stat(depfile);
           if (depfile_mtime > restat_mtime)
             restat_mtime = depfile_mtime;
         }
@@ -830,9 +831,10 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
       }
     }
 
-    // delete the response file on success (if exists)
-    if (edge->HasRspFile())
-      disk_interface_->RemoveFile(edge->GetRspFile());
+    // Delete the response file on success (if exists)
+    string rspfile = edge->GetBinding("rspfile");
+    if (!rspfile.empty())
+      disk_interface_->RemoveFile(rspfile);
 
     plan_.EdgeFinished(edge);
   }
diff --git a/src/clean.cc b/src/clean.cc
index 0b8476b..12afb98 100644
--- a/src/clean.cc
+++ b/src/clean.cc
@@ -83,11 +83,11 @@ bool Cleaner::IsAlreadyRemoved(const string& path) {
 }
 
 void Cleaner::RemoveEdgeFiles(Edge* edge) {
-  string depfile = edge->EvaluateDepFile();
+  string depfile = edge->GetBinding("depfile");
   if (!depfile.empty())
     Remove(depfile);
 
-  string rspfile = edge->GetRspFile();
+  string rspfile = edge->GetBinding("rspfile");
   if (!rspfile.empty())
     Remove(rspfile);
 }
@@ -117,7 +117,7 @@ int Cleaner::CleanAll(bool generator) {
     if ((*e)->is_phony())
       continue;
     // Do not remove generator's files unless generator specified.
-    if (!generator && (*e)->rule().generator())
+    if (!generator && (*e)->GetBindingBool("generator"))
       continue;
     for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
          out_node != (*e)->outputs_.end(); ++out_node) {
diff --git a/src/eval_env.cc b/src/eval_env.cc
index 81a8765..834b7e1 100644
--- a/src/eval_env.cc
+++ b/src/eval_env.cc
@@ -27,6 +27,22 @@ void BindingEnv::AddBinding(const string& key, const string& val) {
   bindings_[key] = val;
 }
 
+string BindingEnv::LookupWithFallback(const string& var,
+                                      const EvalString* eval,
+                                      Env* env) {
+  map<string, string>::iterator i = bindings_.find(var);
+  if (i != bindings_.end())
+    return i->second;
+
+  if (eval)
+    return eval->Evaluate(env);
+
+  if (parent_)
+    return parent_->LookupVariable(var);
+
+  return "";
+}
+
 string EvalString::Evaluate(Env* env) const {
   string result;
   for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) {
diff --git a/src/eval_env.h b/src/eval_env.h
index 6e0a0c0..f3c959a 100644
--- a/src/eval_env.h
+++ b/src/eval_env.h
@@ -22,6 +22,8 @@ using namespace std;
 
 #include "string_piece.h"
 
+struct EvalString;
+
 /// An interface for a scope for variable (e.g. "$foo") lookups.
 struct Env {
   virtual ~Env() {}
@@ -33,10 +35,20 @@ struct Env {
 struct BindingEnv : public Env {
   BindingEnv() : parent_(NULL) {}
   explicit BindingEnv(Env* parent) : parent_(parent) {}
+
   virtual ~BindingEnv() {}
   virtual string LookupVariable(const string& var);
+
   void AddBinding(const string& key, const string& val);
 
+  /// This is tricky.  Edges want lookup scope to go in this order:
+  /// 1) value set on edge itself (edge_->env_)
+  /// 2) value set on rule, with expansion in the edge's scope
+  /// 3) value set on enclosing scope of edge (edge_->env_->parent_)
+  /// This function takes as parameters the necessary info to do (2).
+  string LookupWithFallback(const string& var, const EvalString* eval,
+                            Env* env);
+
 private:
   map<string, string> bindings_;
   Env* parent_;
diff --git a/src/graph.cc b/src/graph.cc
index f9b9c6f..380ca7c 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -32,16 +32,40 @@ bool Node::Stat(DiskInterface* disk_interface) {
   return mtime_ > 0;
 }
 
+void Rule::AddBinding(const string& key, const EvalString& val) {
+  bindings_[key] = val;
+}
+
+const EvalString* Rule::GetBinding(const string& key) const {
+  map<string, EvalString>::const_iterator i = bindings_.find(key);
+  if (i == bindings_.end())
+    return NULL;
+  return &i->second;
+}
+
+// static
+bool Rule::IsReservedBinding(const string& var) {
+  return var == "command" ||
+      var == "depfile" ||
+      var == "description" ||
+      var == "generator" ||
+      var == "pool" ||
+      var == "restat" ||
+      var == "rspfile" ||
+      var == "rspfile_content";
+}
+
 bool DependencyScan::RecomputeDirty(Edge* edge, string* err) {
   bool dirty = false;
   edge->outputs_ready_ = true;
 
-  if (!edge->rule_->depfile().empty()) {
-    if (!LoadDepFile(edge, err)) {
+  string depfile = edge->GetBinding("depfile");
+  if (!depfile.empty()) {
+    if (!LoadDepFile(edge, depfile, err)) {
       if (!err->empty())
         return false;
       EXPLAIN("Edge targets are dirty because depfile '%s' is missing",
-              edge->EvaluateDepFile().c_str());
+              depfile.c_str());
       dirty = true;
     }
   }
@@ -142,7 +166,7 @@ bool DependencyScan::RecomputeOutputDirty(Edge* edge,
     // build log.  Use that mtime instead, so that the file will only be
     // considered dirty if an input was modified since the previous run.
     TimeStamp most_recent_stamp = most_recent_input->mtime();
-    if (edge->rule_->restat() && build_log() &&
+    if (edge->GetBindingBool("restat") && build_log() &&
         (entry = build_log()->LookupByOutput(output->path()))) {
       if (entry->restat_mtime < most_recent_stamp) {
         EXPLAIN("restat of output %s older than most recent input %s "
@@ -162,7 +186,7 @@ bool DependencyScan::RecomputeOutputDirty(Edge* edge,
   // May also be dirty due to the command changing since the last build.
   // But if this is a generator rule, the command changing does not make us
   // dirty.
-  if (!edge->rule_->generator() && build_log()) {
+  if (!edge->GetBindingBool("generator") && build_log()) {
     if (entry || (entry = build_log()->LookupByOutput(output->path()))) {
       if (BuildLog::LogEntry::HashCommand(command) != entry->command_hash) {
         EXPLAIN("command line changed for %s", output->path().c_str());
@@ -212,11 +236,11 @@ string EdgeEnv::LookupVariable(const string& var) {
     return MakePathList(edge_->outputs_.begin(),
                         edge_->outputs_.end(),
                         ' ');
-  } else if (edge_->env_) {
-    return edge_->env_->LookupVariable(var);
-  } else {
-    return string();
   }
+
+  // See notes on BindingEnv::LookupWithFallback.
+  const EvalString* eval = edge_->rule_->GetBinding(var);
+  return edge_->env_->LookupWithFallback(var, eval, this);
 }
 
 string EdgeEnv::MakePathList(vector<Node*>::iterator begin,
@@ -239,40 +263,26 @@ string EdgeEnv::MakePathList(vector<Node*>::iterator begin,
 }
 
 string Edge::EvaluateCommand(bool incl_rsp_file) {
-  EdgeEnv env(this);
-  string command = rule_->command().Evaluate(&env);
-  if (incl_rsp_file && HasRspFile())
-    command += ";rspfile=" + GetRspFileContent();
+  string command = GetBinding("command");
+  if (incl_rsp_file) {
+    string rspfile_content = GetBinding("rspfile_content");
+    if (!rspfile_content.empty())
+      command += ";rspfile=" + rspfile_content;
+  }
   return command;
 }
 
-string Edge::EvaluateDepFile() {
+string Edge::GetBinding(const string& key) {
   EdgeEnv env(this);
-  return rule_->depfile().Evaluate(&env);
+  return env.LookupVariable(key);
 }
 
-string Edge::GetDescription() {
-  EdgeEnv env(this);
-  return rule_->description().Evaluate(&env);
-}
-
-bool Edge::HasRspFile() {
-  return !rule_->rspfile().empty();
-}
-
-string Edge::GetRspFile() {
-  EdgeEnv env(this);
-  return rule_->rspfile().Evaluate(&env);
-}
-
-string Edge::GetRspFileContent() {
-  EdgeEnv env(this);
-  return rule_->rspfile_content().Evaluate(&env);
+bool Edge::GetBindingBool(const string& key) {
+  return !GetBinding(key).empty();
 }
 
-bool DependencyScan::LoadDepFile(Edge* edge, string* err) {
+bool DependencyScan::LoadDepFile(Edge* edge, const string& path, string* err) {
   METRIC_RECORD("depfile load");
-  string path = edge->EvaluateDepFile();
   string content = disk_interface_->ReadFile(path, err);
   if (!err->empty())
     return false;
@@ -317,8 +327,7 @@ bool DependencyScan::LoadDepFile(Edge* edge, string* err) {
     // create one; this makes us not abort if the input is missing,
     // but instead will rebuild in that circumstance.
     if (!node->in_edge()) {
-      Edge* phony_edge = state_->AddEdge(&State::kPhonyRule,
-                                         &State::kDefaultPool);
+      Edge* phony_edge = state_->AddEdge(&State::kPhonyRule);
       node->set_in_edge(phony_edge);
       phony_edge->outputs_.push_back(node);
 
diff --git a/src/graph.h b/src/graph.h
index 3c31e19..8b93e29 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -102,38 +102,23 @@ private:
 
 /// An invokable build command and associated metadata (description, etc.).
 struct Rule {
-  explicit Rule(const string& name)
-      : name_(name), generator_(false), restat_(false) {}
+  explicit Rule(const string& name) : name_(name) {}
 
   const string& name() const { return name_; }
 
-  bool generator() const { return generator_; }
-  bool restat() const { return restat_; }
+  typedef map<string, EvalString> Bindings;
+  void AddBinding(const string& key, const EvalString& val);
 
-  const EvalString& command() const { return command_; }
-  const EvalString& description() const { return description_; }
-  const EvalString& depfile() const { return depfile_; }
-  const EvalString& rspfile() const { return rspfile_; }
-  const EvalString& rspfile_content() const { return rspfile_content_; }
+  static bool IsReservedBinding(const string& var);
 
-  /// Used by a test.
-  void set_command(const EvalString& command) { command_ = command; }
+  const EvalString* GetBinding(const string& key) const;
 
  private:
   // Allow the parsers to reach into this object and fill out its fields.
   friend struct ManifestParser;
 
   string name_;
-
-  bool generator_;
-  bool restat_;
-
-  EvalString command_;
-  EvalString description_;
-  EvalString depfile_;
-  EvalString pool_;
-  EvalString rspfile_;
-  EvalString rspfile_content_;
+  map<string, EvalString> bindings_;
 };
 
 struct BuildLog;
@@ -153,17 +138,9 @@ struct Edge {
   /// If incl_rsp_file is enabled, the string will also contain the
   /// full contents of a response file (if applicable)
   string EvaluateCommand(bool incl_rsp_file = false);
-  string EvaluateDepFile();
-  string GetDescription();
-
-  /// Does the edge use a response file?
-  bool HasRspFile();
-
-  /// Get the path to the response file
-  string GetRspFile();
 
-  /// Get the contents of the response file
-  string GetRspFileContent();
+  string GetBinding(const string& key);
+  bool GetBindingBool(const string& key);
 
   void Dump(const char* prefix="") const;
 
@@ -171,7 +148,7 @@ struct Edge {
   Pool* pool_;
   vector<Node*> inputs_;
   vector<Node*> outputs_;
-  Env* env_;
+  BindingEnv* env_;
   bool outputs_ready_;
 
   const Rule& rule() const { return *rule_; }
@@ -220,7 +197,7 @@ struct DependencyScan {
   bool RecomputeOutputDirty(Edge* edge, Node* most_recent_input,
                             const string& command, Node* output);
 
-  bool LoadDepFile(Edge* edge, string* err);
+  bool LoadDepFile(Edge* edge, const string& path, string* err);
 
   BuildLog* build_log() const {
     return build_log_;
diff --git a/src/graph_test.cc b/src/graph_test.cc
index 5b25c2f..396def4 100644
--- a/src/graph_test.cc
+++ b/src/graph_test.cc
@@ -187,3 +187,38 @@ TEST_F(GraphTest, DepfileRemoved) {
   ASSERT_EQ("", err);
   EXPECT_TRUE(GetNode("out.o")->dirty());
 }
+
+// Check that rule-level variables are in scope for eval.
+TEST_F(GraphTest, RuleVariablesInScope) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule r\n"
+"  depfile = x\n"
+"  command = depfile is $depfile\n"
+"build out: r in\n"));
+  Edge* edge = GetNode("out")->in_edge();
+  EXPECT_EQ("depfile is x", edge->EvaluateCommand());
+}
+
+// Check that build statements can override rule builtins like depfile.
+TEST_F(GraphTest, DepfileOverride) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule r\n"
+"  depfile = x\n"
+"  command = unused\n"
+"build out: r in\n"
+"  depfile = y\n"));
+  Edge* edge = GetNode("out")->in_edge();
+  EXPECT_EQ("y", edge->GetBinding("depfile"));
+}
+
+// Check that overridden values show up in expansion of rule-level bindings.
+TEST_F(GraphTest, DepfileOverrideParent) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule r\n"
+"  depfile = x\n"
+"  command = depfile is $depfile\n"
+"build out: r in\n"
+"  depfile = y\n"));
+  Edge* edge = GetNode("out")->in_edge();
+  EXPECT_EQ("depfile is y", edge->GetBinding("command"));
+}
diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc
index 2d052b5..9d17932 100644
--- a/src/manifest_parser.cc
+++ b/src/manifest_parser.cc
@@ -154,22 +154,8 @@ bool ManifestParser::ParseRule(string* err) {
     if (!ParseLet(&key, &value, err))
       return false;
 
-    if (key == "command") {
-      rule->command_ = value;
-    } else if (key == "depfile") {
-      rule->depfile_ = value;
-    } else if (key == "description") {
-      rule->description_ = value;
-    } else if (key == "generator") {
-      rule->generator_ = true;
-    } else if (key == "restat") {
-      rule->restat_ = true;
-    } else if (key == "rspfile") {
-      rule->rspfile_ = value;
-    } else if (key == "rspfile_content") {
-      rule->rspfile_content_ = value;
-    } else if (key == "pool") {
-      rule->pool_ = value;
+    if (Rule::IsReservedBinding(key)) {
+      rule->AddBinding(key, value);
     } else {
       // Die on other keyvals for now; revisit if we want to add a
       // scope here.
@@ -177,12 +163,13 @@ bool ManifestParser::ParseRule(string* err) {
     }
   }
 
-  if (rule->rspfile_.empty() != rule->rspfile_content_.empty()) {
-    return lexer_.Error("rspfile and rspfile_content need to be both specified",
-                        err);
+  if (rule->bindings_["rspfile"].empty() !=
+      rule->bindings_["rspfile_content"].empty()) {
+    return lexer_.Error("rspfile and rspfile_content need to be "
+                        "both specified", err);
   }
 
-  if (rule->command_.empty())
+  if (rule->bindings_["command"].empty())
     return lexer_.Error("expected 'command =' line", err);
 
   state_->AddRule(rule);
@@ -296,42 +283,29 @@ bool ManifestParser::ParseEdge(string* err) {
   if (!ExpectToken(Lexer::NEWLINE, err))
     return false;
 
-  // Default to using outer env.
-  BindingEnv* env = env_;
-  Pool* pool = NULL;
+  // XXX scoped_ptr to handle error case.
+  BindingEnv* env = new BindingEnv(env_);
 
-  // But create and fill a nested env if there are variables in scope.
-  if (lexer_.PeekToken(Lexer::INDENT)) {
-    // XXX scoped_ptr to handle error case.
-    env = new BindingEnv(env_);
-    do {
-      string key;
-      EvalString val;
-      if (!ParseLet(&key, &val, err))
-        return false;
-      if (key == "pool") {
-        string pool_name = val.Evaluate(env_);
-        pool = state_->LookupPool(pool_name);
-        if (pool == NULL)
-          return lexer_.Error("undefined pool '" + pool_name + "'", err);
-      } else {
-        env->AddBinding(key, val.Evaluate(env_));
-      }
-    } while (lexer_.PeekToken(Lexer::INDENT));
-  }
+  while (lexer_.PeekToken(Lexer::INDENT)) {
+    string key;
+    EvalString val;
+    if (!ParseLet(&key, &val, err))
+      return false;
 
-  if (pool == NULL) {
-    if (!rule->pool_.empty()) {
-      pool = state_->LookupPool(rule->pool_.Evaluate(env_));
-      if (pool == NULL)
-        return lexer_.Error("cannot resolve pool for this edge.", err);
-    } else {
-      pool = &State::kDefaultPool;
-    }
+    env->AddBinding(key, val.Evaluate(env_));
   }
 
-  Edge* edge = state_->AddEdge(rule, pool);
+  Edge* edge = state_->AddEdge(rule);
   edge->env_ = env;
+
+  string pool_name = edge->GetBinding("pool");
+  if (!pool_name.empty()) {
+    Pool* pool = state_->LookupPool(pool_name);
+    if (pool == NULL)
+      return lexer_.Error("unknown pool name", err);
+    edge->pool_ = pool;
+  }
+
   for (vector<EvalString>::iterator i = ins.begin(); i != ins.end(); ++i) {
     string path = i->Evaluate(env);
     string path_err;
diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index 8b00efb..92f52d2 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -61,7 +61,8 @@ TEST_F(ParserTest, Rules) {
   ASSERT_EQ(3u, state.rules_.size());
   const Rule* rule = state.rules_.begin()->second;
   EXPECT_EQ("cat", rule->name());
-  EXPECT_EQ("[cat ][$in][ > ][$out]", rule->command().Serialize());
+  EXPECT_EQ("[cat ][$in][ > ][$out]",
+            rule->GetBinding("command")->Serialize());
 }
 
 TEST_F(ParserTest, RuleAttributes) {
@@ -92,8 +93,9 @@ TEST_F(ParserTest, IgnoreIndentedComments) {
   ASSERT_EQ(2u, state.rules_.size());
   const Rule* rule = state.rules_.begin()->second;
   EXPECT_EQ("cat", rule->name());
-  EXPECT_TRUE(rule->restat());
-  EXPECT_FALSE(rule->generator());
+  Edge* edge = state.GetNode("result")->in_edge();
+  EXPECT_TRUE(edge->GetBindingBool("restat"));
+  EXPECT_FALSE(edge->GetBindingBool("generator"));
 }
 
 TEST_F(ParserTest, IgnoreIndentedBlankLines) {
@@ -124,9 +126,10 @@ TEST_F(ParserTest, ResponseFiles) {
   ASSERT_EQ(2u, state.rules_.size());
   const Rule* rule = state.rules_.begin()->second;
   EXPECT_EQ("cat_rsp", rule->name());
-  EXPECT_EQ("[cat ][$rspfile][ > ][$out]", rule->command().Serialize());
-  EXPECT_EQ("[$rspfile]", rule->rspfile().Serialize());
-  EXPECT_EQ("[$in]", rule->rspfile_content().Serialize());
+  EXPECT_EQ("[cat ][$rspfile][ > ][$out]",
+            rule->GetBinding("command")->Serialize());
+  EXPECT_EQ("[$rspfile]", rule->GetBinding("rspfile")->Serialize());
+  EXPECT_EQ("[$in]", rule->GetBinding("rspfile_content")->Serialize());
 }
 
 TEST_F(ParserTest, InNewline) {
@@ -140,7 +143,8 @@ TEST_F(ParserTest, InNewline) {
   ASSERT_EQ(2u, state.rules_.size());
   const Rule* rule = state.rules_.begin()->second;
   EXPECT_EQ("cat_rsp", rule->name());
-  EXPECT_EQ("[cat ][$in_newline][ > ][$out]", rule->command().Serialize());
+  EXPECT_EQ("[cat ][$in_newline][ > ][$out]",
+            rule->GetBinding("command")->Serialize());
 
   Edge* edge = state.edges_[0];
   EXPECT_EQ("cat in\nin2 > out", edge->EvaluateCommand());
@@ -200,7 +204,7 @@ TEST_F(ParserTest, Continuation) {
   ASSERT_EQ(2u, state.rules_.size());
   const Rule* rule = state.rules_.begin()->second;
   EXPECT_EQ("link", rule->name());
-  EXPECT_EQ("[foo bar baz]", rule->command().Serialize());
+  EXPECT_EQ("[foo bar baz]", rule->GetBinding("command")->Serialize());
 }
 
 TEST_F(ParserTest, Backslash) {
diff --git a/src/state.cc b/src/state.cc
index bb0cc15..b5d2622 100644
--- a/src/state.cc
+++ b/src/state.cc
@@ -91,10 +91,10 @@ Pool* State::LookupPool(const string& pool_name) {
   return i->second;
 }
 
-Edge* State::AddEdge(const Rule* rule, Pool* pool) {
+Edge* State::AddEdge(const Rule* rule) {
   Edge* edge = new Edge();
   edge->rule_ = rule;
-  edge->pool_ = pool;
+  edge->pool_ = &State::kDefaultPool;
   edge->env_ = &bindings_;
   edges_.push_back(edge);
   return edge;
diff --git a/src/state.h b/src/state.h
index 918fe09..326fbac 100644
--- a/src/state.h
+++ b/src/state.h
@@ -92,7 +92,7 @@ struct State {
   void AddPool(Pool* pool);
   Pool* LookupPool(const string& pool_name);
 
-  Edge* AddEdge(const Rule* rule, Pool* pool);
+  Edge* AddEdge(const Rule* rule);
 
   Node* GetNode(StringPiece path);
   Node* LookupNode(StringPiece path);
diff --git a/src/state_test.cc b/src/state_test.cc
index 26177ff..af2bff1 100644
--- a/src/state_test.cc
+++ b/src/state_test.cc
@@ -29,10 +29,10 @@ TEST(State, Basic) {
   command.AddSpecial("out");
 
   Rule* rule = new Rule("cat");
-  rule->set_command(command);
+  rule->AddBinding("command", command);
   state.AddRule(rule);
 
-  Edge* edge = state.AddEdge(rule, &State::kDefaultPool);
+  Edge* edge = state.AddEdge(rule);
   state.AddIn(edge, "in1");
   state.AddIn(edge, "in2");
   state.AddOut(edge, "out");
-- 
cgit v0.12


From 94ea3e9d087ced80aaa62ed25fd239be825814a0 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Tue, 30 Oct 2012 09:52:40 -0700
Subject: update docs to clarify scoping rules

---
 doc/manual.asciidoc | 52 +++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 35 insertions(+), 17 deletions(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 42e5452..bfcb13b 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -747,6 +747,31 @@ header file before starting a subsequent compilation step.  (Once the
 header is used in compilation, a generated dependency file will then
 express the implicit dependency.)
 
+Variable expansion
+~~~~~~~~~~~~~~~~~~
+
+Variables are expanded in paths (in a `build` or `default` statement)
+and on the right side of a `name = value` statement.
+
+When a `name = value` statement is evaluated, its right-hand side is
+expanded immediately (according to the below scoping rules), and
+from then on `$name` expands to the static string as the result of the
+expansion.  It is never the case that you'll need to "double-escape" a
+value to prevent it from getting expanded twice.
+
+All variables are expanded immediately as they're encountered in parsing,
+with one important exception: variables in `rule` blocks are expanded
+when the rule is _used_, not when it is declared.  In the following
+example, the `demo` rule prints "this is a demo of bar".
+
+----
+rule demo
+  command = echo "this is a demo of $foo'
+
+build out: demo
+  foo = bar
+----
+
 Evaluation and scoping
 ~~~~~~~~~~~~~~~~~~~~~~
 [[ref_scope]]
@@ -762,26 +787,19 @@ To include another `.ninja` file in the current scope, much like a C
 `#include` statement, use `include` instead of `subninja`.
 
 Variable declarations indented in a `build` block are scoped to the
-`build` block.  This scope is inherited by the `rule`.  The full
-lookup order for a variable referenced in a rule is:
+`build` block.  The full lookup order for a variable expanded in a
+`build` block (or the `rule` is uses) is:
 
-1. Rule-level variables (i.e. `$in`, `$command`).
+1. Special built-in variables (`$in`, `$out`).
 
-2. Build-level variables from the `build` that references this rule.
+2. Build-level variables from the `build` block.
 
-3. File-level variables from the file that the `build` line was in.
+3. Rule-level variables from the `rule` block (i.e. `$command`).
+   (Note from the above discussion on expansion that these are
+   expanded "late", and may make use of in-scope bindings like `$in`.)
 
-4. Variables from the file that included that file using the
-   `subninja` keyword.
+4. File-level variables from the file that the `build` line was in.
 
-Variable expansion
-~~~~~~~~~~~~~~~~~~
-
-Variables are expanded in paths (in a `build` or `default` statement)
-and on the right side of a `name = value` statement.
+5. Variables from the file that included that file using the
+   `subninja` keyword.
 
-When a `name = value` statement is evaluated, its right-hand side is
-expanded once (according to the above scoping rules) immediately, and
-from then on `$name` expands to the static string as the result of the
-expansion.  It is never the case that you'll need to "double-escape" a
-value to prevent it from getting expanded twice.
-- 
cgit v0.12


From 34b46f28c5496def6f8c72bf96bd30830e5477ef Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Wed, 31 Oct 2012 15:18:36 -0700
Subject: drop the "rules" tool completely

I had already broken this earlier, but the refactor of variable handling
broke it completely.
---
 doc/manual.asciidoc |  4 ----
 src/ninja.cc        | 19 -------------------
 2 files changed, 23 deletions(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index bfcb13b..d51c0ce 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -263,10 +263,6 @@ several times. If used like this +ninja -t targets all+ it
 prints all the targets available without indentation and it is faster
 than the _depth_ mode.
 
-`rules`:: output the list of all rules with their description if they have
-one.  It can be used to know which rule name to pass to
-+ninja -t targets rule _name_+.
-
 `commands`:: given a list of targets, print a list of commands which, if
 executed in order, may be used to rebuild those targets, assuming that all
 output files are out of date.
diff --git a/src/ninja.cc b/src/ninja.cc
index 38f1d78..324b884 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -410,23 +410,6 @@ int ToolTargets(Globals* globals, int argc, char* argv[]) {
   }
 }
 
-int ToolRules(Globals* globals, int argc, char* /* argv */[]) {
-  for (map<string, const Rule*>::iterator i = globals->state->rules_.begin();
-       i != globals->state->rules_.end(); ++i) {
-    if (i->second->description().empty()) {
-      printf("%s\n", i->first.c_str());
-    } else {
-      printf("%s: %s\n",
-             i->first.c_str(),
-             // XXX I changed it such that we don't have an easy way
-             // to get the source text anymore, so this output is
-             // unsatisfactory.  How useful is this command, anyway?
-             i->second->description().Serialize().c_str());
-    }
-  }
-  return 0;
-}
-
 void PrintCommands(Edge* edge, set<Edge*>* seen) {
   if (!edge)
     return;
@@ -551,8 +534,6 @@ int ChooseTool(const string& tool_name, const Tool** tool_out) {
       Tool::RUN_AFTER_LOAD, ToolGraph },
     { "query", "show inputs/outputs for a path",
       Tool::RUN_AFTER_LOAD, ToolQuery },
-    { "rules",    "list all rules",
-      Tool::RUN_AFTER_LOAD, ToolRules },
     { "targets",  "list targets by their rule or depth in the DAG",
       Tool::RUN_AFTER_LOAD, ToolTargets },
     { "urtle", NULL,
-- 
cgit v0.12


From e38eb5438151b5accc43e7e5d91f1435a2450f2f Mon Sep 17 00:00:00 2001
From: Pino Toscano <pino@debian.org>
Date: Sat, 29 Dec 2012 14:32:27 -0800
Subject: fix build on non-Linux glibc systems

ninja-build does not build on non-Linux archs, such as
GNU/kFreeBSD and GNU/Hurd.

The problem is that the GetProcessorCount() implementation for these
architectures is the sysconf() one, but <unistd.h> has not been
included, causing sysconf() and _SC_NPROCESSORS_ONLN to not be
declared. Another solution (which is the one I chose) is to make use
of the "linux" implementation which uses get_nprocs(), which is a GNU
extension and thus available for anything using GNU libc.
---
 src/util.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/util.cc b/src/util.cc
index 4b2900f..50e3842 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -39,7 +39,7 @@
 #elif defined(__SVR4) && defined(__sun)
 #include <unistd.h>
 #include <sys/loadavg.h>
-#elif defined(linux)
+#elif defined(linux) || defined(__GLIBC__)
 #include <sys/sysinfo.h>
 #endif
 
@@ -295,7 +295,7 @@ string StripAnsiEscapeCodes(const string& in) {
   return stripped;
 }
 
-#if defined(linux)
+#if defined(linux) || defined(__GLIBC__)
 int GetProcessorCount() {
   return get_nprocs();
 }
-- 
cgit v0.12


From 37b5ac7a1a8ca493edd863133e2e9f603c37dfa5 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sat, 29 Dec 2012 16:37:12 -0800
Subject: always set GTEST_HAS_RTTI=0 in all testing code

---
 configure.py | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/configure.py b/configure.py
index 9391a68..f530ebf 100755
--- a/configure.py
+++ b/configure.py
@@ -315,7 +315,7 @@ all_targets += ninja
 n.comment('Tests all build into ninja_test executable.')
 
 variables = []
-test_cflags = cflags[:]
+test_cflags = cflags + ['-DGTEST_HAS_RTTI=0']
 test_ldflags = None
 test_libs = libs
 objs = []
@@ -335,14 +335,12 @@ if options.with_gtest:
                     variables=[('cflags', gtest_cflags)])
 
     test_cflags.append('-I%s' % os.path.join(path, 'include'))
-elif platform == 'windows':
-    test_libs.extend(['gtest_main.lib', 'gtest.lib'])
 else:
-    test_cflags.append('-DGTEST_HAS_RTTI=0')
-    test_libs.extend(['-lgtest_main', '-lgtest'])
-
-if test_cflags == cflags:
-    test_cflags = None
+    # Use gtest from system.
+    if platform == 'windows':
+        test_libs.extend(['gtest_main.lib', 'gtest.lib'])
+    else:
+        test_libs.extend(['-lgtest_main', '-lgtest'])
 
 n.variable('test_cflags', test_cflags)
 for name in ['build_log_test',
-- 
cgit v0.12


From 0fd3797f148a5ec1032f3a8f3b2747e8958128a0 Mon Sep 17 00:00:00 2001
From: Nico Weber <nicolasweber@gmx.de>
Date: Wed, 2 Jan 2013 17:40:12 -0800
Subject: ManifestParser constructor accesses its first argument, don't pass
 NULL

The constructor does

  env_ = &state->bindings_;

so env_ is effectively set to offsetof(ManifestParser, bindings_).  This
will blow up if env_ gets dereferenced -- this doesn't seem to happen in
these tests, but it's less confusing with this patch.  Also, passing &state is
consistent with the rest of this test.
---
 src/manifest_parser_test.cc | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index 92f52d2..4ac093f 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -295,7 +295,8 @@ TEST_F(ParserTest, ReservedWords) {
 
 TEST_F(ParserTest, Errors) {
   {
-    ManifestParser parser(NULL, NULL);
+    State state;
+    ManifestParser parser(&state, NULL);
     string err;
     EXPECT_FALSE(parser.ParseTest("foobar", &err));
     EXPECT_EQ("input:1: expected '=', got eof\n"
@@ -305,7 +306,8 @@ TEST_F(ParserTest, Errors) {
   }
 
   {
-    ManifestParser parser(NULL, NULL);
+    State state;
+    ManifestParser parser(&state, NULL);
     string err;
     EXPECT_FALSE(parser.ParseTest("x 3", &err));
     EXPECT_EQ("input:1: expected '=', got identifier\n"
@@ -315,7 +317,8 @@ TEST_F(ParserTest, Errors) {
   }
 
   {
-    ManifestParser parser(NULL, NULL);
+    State state;
+    ManifestParser parser(&state, NULL);
     string err;
     EXPECT_FALSE(parser.ParseTest("x = 3", &err));
     EXPECT_EQ("input:1: unexpected EOF\n"
-- 
cgit v0.12


From bea333c22fc46f29c44413a7e5ca3f27d0b476d4 Mon Sep 17 00:00:00 2001
From: Patrick von Reth <vonreth@kde.org>
Date: Sat, 12 Jan 2013 11:32:45 +0100
Subject: if windows and not msvc, set platform to mingw

---
 bootstrap.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/bootstrap.py b/bootstrap.py
index a847df9..331ea0f 100755
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -96,6 +96,7 @@ else:
                    '-DNINJA_BOOTSTRAP'])
     if options.windows:
         cflags.append('-D_WIN32_WINNT=0x0501')
+        conf_args.append("--platform=mingw")
     if options.x64:
         cflags.append('-m64')
 args.extend(cflags)
-- 
cgit v0.12


From 3c0b8b2874a0291be8db990fc7e32f0b866adf42 Mon Sep 17 00:00:00 2001
From: Petr Hosek <petrhosek@gmail.com>
Date: Mon, 14 Jan 2013 12:49:05 +0000
Subject: RPM spec file should use correct versioning scheme and list all build
 dependencies

---
 misc/packaging/ninja.spec | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/misc/packaging/ninja.spec b/misc/packaging/ninja.spec
index 2f009f6..3f31480 100644
--- a/misc/packaging/ninja.spec
+++ b/misc/packaging/ninja.spec
@@ -1,13 +1,15 @@
 Summary: Ninja is a small build system with a focus on speed.
 Name: ninja
 Version: %{ver}
-Release: %{rel}%{?dist}
+Release: %{rel}
 Group: Development/Tools
 License: Apache 2.0
 URL: https://github.com/martine/ninja
 Source0: %{name}-%{version}-%{release}.tar.gz
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
 
+BuildRequires: re2c asciidoc
+
 %description
 Ninja is yet another build system. It takes as input the interdependencies of files (typically source code and output executables) and
 orchestrates building them, quickly.
-- 
cgit v0.12


From a592a50679d9d2837b47cf0871c4a642dc7f79d6 Mon Sep 17 00:00:00 2001
From: Petr Hosek <petrhosek@gmail.com>
Date: Tue, 15 Jan 2013 23:27:54 +0000
Subject: Use correct Fedora versioning scheme

---
 misc/packaging/ninja.spec | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/misc/packaging/ninja.spec b/misc/packaging/ninja.spec
index 3f31480..0f7aed6 100644
--- a/misc/packaging/ninja.spec
+++ b/misc/packaging/ninja.spec
@@ -1,12 +1,12 @@
 Summary: Ninja is a small build system with a focus on speed.
 Name: ninja
 Version: %{ver}
-Release: %{rel}
+Release: %{rel}%{?dist}
 Group: Development/Tools
 License: Apache 2.0
 URL: https://github.com/martine/ninja
-Source0: %{name}-%{version}-%{release}.tar.gz
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
+Source0: %{name}-%{version}-%{rel}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{rel}
 
 BuildRequires: re2c asciidoc
 
@@ -19,7 +19,7 @@ which has over 30,000 source files and whose other build systems (including one
 seconds to start building after changing one file. Ninja is under a second.
 
 %prep
-%setup -q -n %{name}-%{version}-%{release}
+%setup -q -n %{name}-%{version}-%{rel}
 
 %build
 echo Building..
-- 
cgit v0.12


From e9f4fe4c4c3e5a729a53451591d74c9880084922 Mon Sep 17 00:00:00 2001
From: Petr Hosek <petrhosek@gmail.com>
Date: Mon, 21 Jan 2013 19:32:44 +0000
Subject: Removed re2c build dependency from RPM spec file

---
 misc/packaging/ninja.spec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/misc/packaging/ninja.spec b/misc/packaging/ninja.spec
index 0f7aed6..f0c46fe 100644
--- a/misc/packaging/ninja.spec
+++ b/misc/packaging/ninja.spec
@@ -8,7 +8,7 @@ URL: https://github.com/martine/ninja
 Source0: %{name}-%{version}-%{rel}.tar.gz
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{rel}
 
-BuildRequires: re2c asciidoc
+BuildRequires: asciidoc
 
 %description
 Ninja is yet another build system. It takes as input the interdependencies of files (typically source code and output executables) and
-- 
cgit v0.12


From efee6733acc535731f3137442dc4600797460a27 Mon Sep 17 00:00:00 2001
From: Scott Graham <scottmg@chromium.org>
Date: Thu, 24 Jan 2013 09:33:21 -0800
Subject: more verbose error (including path) when depfile fails to load

---
 src/graph.cc | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/graph.cc b/src/graph.cc
index 380ca7c..b000c48 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -284,8 +284,10 @@ bool Edge::GetBindingBool(const string& key) {
 bool DependencyScan::LoadDepFile(Edge* edge, const string& path, string* err) {
   METRIC_RECORD("depfile load");
   string content = disk_interface_->ReadFile(path, err);
-  if (!err->empty())
+  if (!err->empty()) {
+    *err = "loading '" + path + "': " + *err;
     return false;
+  }
   // On a missing depfile: return false and empty *err.
   if (content.empty())
     return false;
-- 
cgit v0.12


From 447f7050d062e8b22038ba203ca822ca10da93a6 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Fri, 1 Feb 2013 10:31:46 -0800
Subject: add a release checklist so I won't forget steps in the future

---
 RELEASING | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 RELEASING

diff --git a/RELEASING b/RELEASING
new file mode 100644
index 0000000..3f83c7b
--- /dev/null
+++ b/RELEASING
@@ -0,0 +1,9 @@
+Notes to myself on all the steps to make for a Ninja release.
+
+1. git checkout release; git merge master
+2. fix version number in source (it will likely conflict in the above)
+3. fix version in doc/manual.asciidoc
+4. rebuild manual, put in place on website
+5. commit, tag, push
+6. construct release notes from prior notes
+   credits: git shortlog -s --no-merges REV..
-- 
cgit v0.12


From 05736568b9a26599865dd7452e62f70ecdbc9e41 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Fri, 1 Feb 2013 10:37:10 -0800
Subject: mark pools as experimental in the docs

---
 doc/manual.asciidoc | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index d51c0ce..6b1de9b 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -420,6 +420,10 @@ before building the targets requested by the user.
 Pools
 ~~~~~
 
+*Note: pools were added as an experiment and may be removed in a future
+version of Ninja.  Are they useful for you?  Let us know on the mailing
+list.*
+
 Pools allow you to allocate one or more rules or edges a finite number
 of concurrent jobs which is more tightly restricted than the default
 parallelism.
-- 
cgit v0.12


From bed9541e7f5123612dbcb1840ee096a92c2e18dc Mon Sep 17 00:00:00 2001
From: Paul Kunysch <kunysch@me.com>
Date: Sat, 9 Feb 2013 13:19:13 +0100
Subject: util.cc: Reusing windows workaround for cygwin.

This fixes:
src/util.cc: In function 'double GetLoadAverage()':
src/util.cc:337:28: error: 'getloadavg' was not declared in this scope
---
 src/util.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/util.cc b/src/util.cc
index 50e3842..91e8fad 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -325,7 +325,7 @@ int GetProcessorCount() {
 }
 #endif
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
 double GetLoadAverage() {
   // TODO(nicolas.despres@gmail.com): Find a way to implement it on Windows.
   // Remember to also update Usage() when this is fixed.
-- 
cgit v0.12


From 10f3e17b6dc7718ab00552300871bbfe49854c8b Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Tue, 30 Oct 2012 09:40:59 -0700
Subject: add syntax for checking versions

---
 configure.py           |  3 ++-
 src/manifest_parser.cc | 12 ++++++++---
 src/ninja.cc           |  9 +++-----
 src/version.cc         | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/version.h          | 32 +++++++++++++++++++++++++++++
 5 files changed, 102 insertions(+), 10 deletions(-)
 create mode 100644 src/version.cc
 create mode 100644 src/version.h

diff --git a/configure.py b/configure.py
index f530ebf..dc6bfb9 100755
--- a/configure.py
+++ b/configure.py
@@ -279,7 +279,8 @@ for name in ['build',
              'manifest_parser',
              'metrics',
              'state',
-             'util']:
+             'util',
+             'version']:
     objs += cxx(name)
 if platform in ('mingw', 'windows'):
     for name in ['subprocess-win32',
diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc
index 9d17932..14fca73 100644
--- a/src/manifest_parser.cc
+++ b/src/manifest_parser.cc
@@ -23,6 +23,7 @@
 #include "metrics.h"
 #include "state.h"
 #include "util.h"
+#include "version.h"
 
 ManifestParser::ManifestParser(State* state, FileReader* file_reader)
   : state_(state), file_reader_(file_reader) {
@@ -66,10 +67,15 @@ bool ManifestParser::Parse(const string& filename, const string& input,
     case Lexer::IDENT: {
       lexer_.UnreadToken();
       string name;
-      EvalString value;
-      if (!ParseLet(&name, &value, err))
+      EvalString let_value;
+      if (!ParseLet(&name, &let_value, err))
         return false;
-      env_->AddBinding(name, value.Evaluate(env_));
+      string value = let_value.Evaluate(env_);
+      // Check ninja_required_version immediately so we can exit
+      // before encountering any syntactic surprises.
+      if (name == "ninja_required_version")
+        CheckNinjaVersion(value);
+      env_->AddBinding(name, value);
       break;
     }
     case Lexer::INCLUDE:
diff --git a/src/ninja.cc b/src/ninja.cc
index 324b884..06dee8a 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -41,16 +41,13 @@
 #include "metrics.h"
 #include "state.h"
 #include "util.h"
+#include "version.h"
 
 // Defined in msvc_helper_main-win32.cc.
 int MSVCHelperMain(int argc, char** argv);
 
 namespace {
 
-/// The version number of the current Ninja release.  This will always
-/// be "git" on trunk.
-const char* kVersion = "git";
-
 /// Global information passed into subtools.
 struct Globals {
   Globals() : state(new State()) {}
@@ -121,7 +118,7 @@ void Usage(const BuildConfig& config) {
 "  -d MODE  enable debugging (use -d list to list modes)\n"
 "  -t TOOL  run a subtool (use -t list to list subtools)\n"
 "    terminates toplevel options; further flags are passed to the tool\n",
-          kVersion, config.parallelism);
+          kNinjaVersion, config.parallelism);
 }
 
 /// Choose a default value for the -j (parallelism) flag.
@@ -772,7 +769,7 @@ int NinjaMain(int argc, char** argv) {
         working_dir = optarg;
         break;
       case OPT_VERSION:
-        printf("%s\n", kVersion);
+        printf("%s\n", kNinjaVersion);
         return 0;
       case 'h':
       default:
diff --git a/src/version.cc b/src/version.cc
new file mode 100644
index 0000000..7e805c1
--- /dev/null
+++ b/src/version.cc
@@ -0,0 +1,56 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "version.h"
+
+#include <stdlib.h>
+
+#include "util.h"
+
+const char* kNinjaVersion = "1.1.0.git";
+
+void ParseVersion(const string& version, int* major, int* minor) {
+  size_t end = version.find('.');
+  *major = atoi(version.substr(0, end).c_str());
+  *minor = 0;
+  if (end != string::npos) {
+    size_t start = end + 1;
+    end = version.find(start, '.');
+    *minor = atoi(version.substr(start, end).c_str());
+  }
+}
+
+void CheckNinjaVersion(const string& version) {
+  int bin_major, bin_minor;
+  ParseVersion(kNinjaVersion, &bin_major, &bin_minor);
+  int file_major, file_minor;
+  ParseVersion(version, &file_major, &file_minor);
+
+  if (bin_major > file_major) {
+    Warning("ninja executable version (%s) greater than build file "
+            "ninja_required_version (%s); versions may be incompatible.",
+            kNinjaVersion, version.c_str());
+    return;
+  }
+
+  if ((bin_major == file_major && bin_minor < file_minor) ||
+      bin_major < file_major) {
+    Fatal("ninja version (%s) incompatible with build file "
+          "ninja_required_version version (%s).",
+          kNinjaVersion, version.c_str());
+  }
+}
+
+
+
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 0000000..bd6b9ff
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,32 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NINJA_VERSION_H_
+#define NINJA_VERSION_H_
+
+#include <string>
+using namespace std;
+
+/// The version number of the current Ninja release.  This will always
+/// be "git" on trunk.
+extern const char* kNinjaVersion;
+
+/// Parse the major/minor components of a version string.
+void ParseVersion(const string& version, int* major, int* minor);
+
+/// Check whether \a version is compatible with the current Ninja version,
+/// aborting if not.
+void CheckNinjaVersion(const string& required_version);
+
+#endif  // NINJA_VERSION_H_
-- 
cgit v0.12


From 0328a7bc9f8fdc938e45f9bc91377ed2c1c3d035 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sat, 16 Feb 2013 14:59:30 -0800
Subject: docs for ninja_required_version

(And some extra docs for top-level variables in general.)
---
 doc/manual.asciidoc | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 6b1de9b..93e34e2 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -539,6 +539,8 @@ default.
 The Ninja log
 ~~~~~~~~~~~~~
 
+[[ref_log]]
+
 For each built file, Ninja keeps a log of the command used to build
 it.  Using this log Ninja can know when an existing output was built
 with a different command line than the build files specify (i.e., the
@@ -549,6 +551,35 @@ If you provide a variable named `builddir` in the outermost scope,
 `.ninja_log` will be kept in that directory instead.
 
 
+Version compatibility
+~~~~~~~~~~~~~~~~~~~~~
+
+[[ref_versioning]]
+
+Ninja version labels follow the standard major.minor.patch format,
+where the major version is increased on backwards-incompatible
+syntax/behavioral changes and the minor version is increased on new
+behaviors.  Your `build.ninja` may declare a variable named
+`ninja_required_version` that asserts the minimum Ninja version
+required to use the generated file.  For example,
+
+-----
+ninja_required_version = 1.1
+-----
+
+declares that the build file relies on some feature that was
+introduced in Ninja 1.1 (perhaps the `pool` syntax), and that
+Ninja 1.1 or greater must be used to build.  Unlike other Ninja
+variables, this version requirement is checked immediately when
+the variable is encountered in parsing, so it's best to put it
+at the top of the build file.
+
+Ninja always warns if the major versions of Ninja and the
+`ninja_required_version` don't match; a major version change hasn't
+come up yet so it's difficult to predict what behavior might be
+required.
+
+
 Ninja file reference
 --------------------
 
@@ -634,6 +665,19 @@ line.  If a line is indented more than the previous one, it's
 considered part of its parent's scope; if it is indented less than the
 previous one, it closes the previous scope.
 
+Top-level variables
+~~~~~~~~~~~~~~~~~~~
+
+Two variables are significant when declared in the outermost file scope.
+
+`builddir`:: a directory for some Ninja output files.  See <<ref_log,the
+  discussion of the build log>>.  (You can also store other build output
+  in this directory.)
+
+`ninja_required_version`:: the minimum verison of Ninja required to process
+  the build correctly.  See <<ref_versioning,the discussion of versioning>>.
+
+
 Rule variables
 ~~~~~~~~~~~~~~
 [[ref_rule]]
-- 
cgit v0.12


From 4fd0552ac325c5014cfe3316c25d7dc9de7740ad Mon Sep 17 00:00:00 2001
From: Scott Graham <scottmg@chromium.org>
Date: Sat, 16 Feb 2013 15:04:52 -0800
Subject: depfile helper: check errors on writing .d files

Closes #492 and #493.
---
 src/msvc_helper_main-win32.cc | 37 ++++++++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc
index 152450e..0bbe98b 100644
--- a/src/msvc_helper_main-win32.cc
+++ b/src/msvc_helper_main-win32.cc
@@ -44,6 +44,31 @@ void PushPathIntoEnvironment(const string& env_block) {
   }
 }
 
+void WriteDepFileOrDie(const char* object_path, CLWrapper* cl) {
+  string depfile_path = string(object_path) + ".d";
+  FILE* depfile = fopen(depfile_path.c_str(), "w");
+  if (!depfile) {
+    unlink(object_path);
+    Fatal("opening %s: %s", depfile_path.c_str(), GetLastErrorString().c_str());
+  }
+  if (fprintf(depfile, "%s: ", object_path) < 0) {
+    unlink(object_path);
+    fclose(depfile);
+    unlink(depfile_path.c_str());
+    Fatal("writing %s", depfile_path.c_str());
+  }
+  vector<string> headers = cl->GetEscapedResult();
+  for (vector<string>::iterator i = headers.begin(); i != headers.end(); ++i) {
+    if (fprintf(depfile, "%s\n", i->c_str()) < 0) {
+      unlink(object_path);
+      fclose(depfile);
+      unlink(depfile_path.c_str());
+      Fatal("writing %s", depfile_path.c_str());
+    }
+  }
+  fclose(depfile);
+}
+
 }  // anonymous namespace
 
 int MSVCHelperMain(int argc, char** argv) {
@@ -95,17 +120,7 @@ int MSVCHelperMain(int argc, char** argv) {
     cl.SetEnvBlock((void*)env.data());
   int exit_code = cl.Run(command);
 
-  string depfile = string(output_filename) + ".d";
-  FILE* output = fopen(depfile.c_str(), "w");
-  if (!output) {
-    Fatal("opening %s: %s", depfile.c_str(), GetLastErrorString().c_str());
-  }
-  fprintf(output, "%s: ", output_filename);
-  vector<string> headers = cl.GetEscapedResult();
-  for (vector<string>::iterator i = headers.begin(); i != headers.end(); ++i) {
-    fprintf(output, "%s\n", i->c_str());
-  }
-  fclose(output);
+  WriteDepFileOrDie(output_filename, &cl);
 
   return exit_code;
 }
-- 
cgit v0.12


From 370249f528b9b55f089937604a8f6ba482a33e02 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sat, 16 Feb 2013 15:16:50 -0800
Subject: say where the default value for -j comes from

This is a FAQ.
---
 src/ninja.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ninja.cc b/src/ninja.cc
index 06dee8a..2b250b7 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -106,7 +106,7 @@ void Usage(const BuildConfig& config) {
 "  -C DIR   change to DIR before doing anything else\n"
 "  -f FILE  specify input build file [default=build.ninja]\n"
 "\n"
-"  -j N     run N jobs in parallel [default=%d]\n"
+"  -j N     run N jobs in parallel [default=%d, derived from CPUs available]\n"
 "  -l N     do not start new jobs if the load average is greater than N\n"
 #ifdef _WIN32
 "           (not yet implemented on Windows)\n"
-- 
cgit v0.12


From d3c4cf1b36b132a2fc6f79970b32046ac4eaeef9 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sat, 16 Feb 2013 17:13:10 -0800
Subject: move from asciidoc html to asciidoc -> docbook -> html

As best as I can tell this is the easiest way to customize
the asciidoc HTML output.
---
 configure.py    | 11 ++++++++---
 doc/docbook.xsl | 17 +++++++++++++++++
 doc/style.css   | 29 +++++++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 3 deletions(-)
 create mode 100644 doc/docbook.xsl
 create mode 100644 doc/style.css

diff --git a/configure.py b/configure.py
index dc6bfb9..10c6994 100755
--- a/configure.py
+++ b/configure.py
@@ -397,9 +397,14 @@ n.newline()
 
 n.comment('Generate the manual using asciidoc.')
 n.rule('asciidoc',
-       command='asciidoc -a toc -a max-width=45em -o $out $in',
-       description='ASCIIDOC $in')
-manual = n.build(doc('manual.html'), 'asciidoc', doc('manual.asciidoc'))
+       command='asciidoc -b docbook -d book -o $out $in',
+       description='ASCIIDOC $out')
+n.rule('xsltproc',
+       command='xsltproc --nonet doc/docbook.xsl $in > $out',
+       description='XSLTPROC $out')
+xml = n.build(built('manual.xml'), 'asciidoc', doc('manual.asciidoc'))
+manual = n.build(doc('manual.html'), 'xsltproc', xml,
+                 implicit=doc('style.css'))
 n.build('manual', 'phony',
         order_only=manual)
 n.newline()
diff --git a/doc/docbook.xsl b/doc/docbook.xsl
new file mode 100644
index 0000000..8afdc8c
--- /dev/null
+++ b/doc/docbook.xsl
@@ -0,0 +1,17 @@
+<!-- This soup of XML is the minimum customization necessary to make the
+     autogenerated manual look ok. -->
+<!DOCTYPE xsl:stylesheet [
+<!ENTITY css SYSTEM "style.css">
+]>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version='1.0'>
+  <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
+  <xsl:template name="user.head.content"><style>&css;</style></xsl:template>
+  <xsl:template name="body.attributes"></xsl:template>
+  <xsl:param name="generate.toc" select="'book toc'"/>
+  <xsl:param name="chapter.autolabel" select="0" />
+  <xsl:param name="toc.list.type">ul</xsl:param>
+
+  <xsl:output method="html" encoding="utf-8" indent="no"
+              doctype-public=""/>
+</xsl:stylesheet>
diff --git a/doc/style.css b/doc/style.css
new file mode 100644
index 0000000..fc22ec1
--- /dev/null
+++ b/doc/style.css
@@ -0,0 +1,29 @@
+body {
+    margin: 5ex 10ex;
+    max-width: 40em;
+    line-height: 1.4;
+    font-family: sans-serif;
+    font-size: 0.8em;
+}
+h1, h2, h3 {
+    font-weight: normal;
+}
+pre, code {
+    font-family: x, monospace;
+}
+pre {
+    padding: 1ex;
+    background: #eee;
+    border: solid 1px #ddd;
+    min-width: 0;
+    font-size: 90%;
+}
+code {
+    color: #007;
+}
+.chapter {
+    margin-top: 4em;
+}
+p {
+    margin-top: 0;
+}
-- 
cgit v0.12


From e6d618556c1a9042af8cc069911dd248246e9e8e Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sat, 16 Feb 2013 17:32:42 -0800
Subject: rearrange env variable section layout

---
 doc/manual.asciidoc | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 93e34e2..60439fd 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -209,20 +209,22 @@ you don't need to pass `-j`.)
 Environment variables
 ~~~~~~~~~~~~~~~~~~~~~
 
-Ninja supports one environment variable to control its behavior.
+Ninja supports one environment variable to control its behavior:
+`NINJA_STATUS`, the progress status printed before the rule being run.
 
-`NINJA_STATUS`:: The progress status printed before the rule being run.
 Several placeholders are available:
-* `%s`: The number of started edges.
-* `%t`: The total number of edges that must be run to complete the build.
-* `%p`: The percentage of started edges.
-* `%r`: The number of currently running edges.
-* `%u`: The number of remaining edges to start.
-* `%f`: The number of finished edges.
-* `%o`: Overall rate of finished edges per second
-* `%c`: Current rate of finished edges per second (average over builds specified by -j or its default)
-* `%%`: A plain `%` character.
-* The default progress status is `"[%s/%t] "` (note the trailing space
+
+`%s`:: The number of started edges.
+`%t`:: The total number of edges that must be run to complete the build.
+`%p`:: The percentage of started edges.
+`%r`:: The number of currently running edges.
+`%u`:: The number of remaining edges to start.
+`%f`:: The number of finished edges.
+`%o`:: Overall rate of finished edges per second
+`%c`:: Current rate of finished edges per second (average over builds specified by -j or its default)
+`%%`:: A plain `%` character.
+
+The default progress status is `"[%s/%t] "` (note the trailing space
 to separate from the build rule). Another example of possible progress status
 could be `"[%u/%r/%f] "`.
 
-- 
cgit v0.12


From 5f3429b14d50b0e937e37c546e648b54dd1b9a2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Haitao=20Li=20=20=E6=9D=8E=E6=B5=B7=E6=B6=9B?=
 <lihaitao@gmail.com>
Date: Sun, 17 Feb 2013 18:50:16 +0800
Subject: Fix typo in manual

---
 doc/manual.asciidoc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 60439fd..221df47 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -397,7 +397,7 @@ rule cc
 # If left unspecified, builds get the outer $cflags.
 build foo.o: cc foo.c
 
-# But you can can shadow variables like cflags for a particular build.
+# But you can shadow variables like cflags for a particular build.
 build special.o: cc special.c
   cflags = -Wall
 
@@ -636,7 +636,7 @@ across a line break).
 paths, where a space would otherwise separate filenames.  See below.)
 
 `$:` :: a colon.  (This is only necessary in `build` lines, where a colon
-would otherwise terminate the list of inputs.)
+would otherwise terminate the list of outputs.)
 
 `$$`:: a literal `$`.
 
-- 
cgit v0.12


From d56f995ef0b86e46f87e596a87f16b4d636968f5 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sun, 17 Feb 2013 11:16:23 -0800
Subject: make doc xrefs link to section headers

---
 doc/manual.asciidoc | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 60439fd..647f4d2 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -538,11 +538,10 @@ This causes Ninja to build the `foo`, `bar` and `baz` targets by
 default.
 
 
+[[ref_log]]
 The Ninja log
 ~~~~~~~~~~~~~
 
-[[ref_log]]
-
 For each built file, Ninja keeps a log of the command used to build
 it.  Using this log Ninja can know when an existing output was built
 with a different command line than the build files specify (i.e., the
@@ -553,11 +552,10 @@ If you provide a variable named `builddir` in the outermost scope,
 `.ninja_log` will be kept in that directory instead.
 
 
+[[ref_versioning]]
 Version compatibility
 ~~~~~~~~~~~~~~~~~~~~~
 
-[[ref_versioning]]
-
 Ninja version labels follow the standard major.minor.patch format,
 where the major version is increased on backwards-incompatible
 syntax/behavioral changes and the minor version is increased on new
@@ -680,9 +678,9 @@ Two variables are significant when declared in the outermost file scope.
   the build correctly.  See <<ref_versioning,the discussion of versioning>>.
 
 
+[[ref_rule]]
 Rule variables
 ~~~~~~~~~~~~~~
-[[ref_rule]]
 
 A `rule` block contains a list of `key = value` declarations that
 affect the processing of the rule.  Here is a full list of special
@@ -754,9 +752,9 @@ Finally, the special `$in` and `$out` variables expand to the
 shell-quoted space-separated list of files provided to the `build`
 line referencing this `rule`.
 
+[[ref_dependencies]]
 Build dependencies
 ~~~~~~~~~~~~~~~~~~
-[[ref_dependencies]]
 
 There are three types of build dependencies which are subtly different.
 
@@ -818,9 +816,9 @@ build out: demo
   foo = bar
 ----
 
+[[ref_scope]]
 Evaluation and scoping
 ~~~~~~~~~~~~~~~~~~~~~~
-[[ref_scope]]
 
 Top-level variable declarations are scoped to the file they occur in.
 
-- 
cgit v0.12


From 52ae6ddb18c02fd06535574c265f4acc5b97dd4a Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sun, 17 Feb 2013 11:22:37 -0800
Subject: add versioning notes to features in the manual

This helps guide which ninja version to depend on.
---
 RELEASING           | 7 ++++---
 doc/manual.asciidoc | 6 +++---
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/RELEASING b/RELEASING
index 3f83c7b..0593e6d 100644
--- a/RELEASING
+++ b/RELEASING
@@ -3,7 +3,8 @@ Notes to myself on all the steps to make for a Ninja release.
 1. git checkout release; git merge master
 2. fix version number in source (it will likely conflict in the above)
 3. fix version in doc/manual.asciidoc
-4. rebuild manual, put in place on website
-5. commit, tag, push
-6. construct release notes from prior notes
+4. grep doc/manual.asciidoc for XXX, fix version references
+5. rebuild manual, put in place on website
+6. commit, tag, push
+7. construct release notes from prior notes
    credits: git shortlog -s --no-merges REV..
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 647f4d2..5a66904 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -422,9 +422,7 @@ before building the targets requested by the user.
 Pools
 ~~~~~
 
-*Note: pools were added as an experiment and may be removed in a future
-version of Ninja.  Are they useful for you?  Let us know on the mailing
-list.*
+_Available since Ninja 1.1._
 
 Pools allow you to allocate one or more rules or edges a finite number
 of concurrent jobs which is more tightly restricted than the default
@@ -556,6 +554,8 @@ If you provide a variable named `builddir` in the outermost scope,
 Version compatibility
 ~~~~~~~~~~~~~~~~~~~~~
 
+_Available since Ninja 1.XXX._
+
 Ninja version labels follow the standard major.minor.patch format,
 where the major version is increased on backwards-incompatible
 syntax/behavioral changes and the minor version is increased on new
-- 
cgit v0.12


From e758e8d0e014c1442ab5973541a3a1cc59dc97cb Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Tue, 19 Feb 2013 09:31:16 -0800
Subject: manual: move in_newline to the reference

It's a detail, it doesn't belong in the brief tutorial overview.
---
 doc/manual.asciidoc | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 5a66904..1e5b70b 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -356,17 +356,11 @@ consisting of the `rule` keyword and a name for the rule.  Then
 follows an indented set of `variable = value` lines.
 
 The basic example above declares a new rule named `cc`, along with the
-command to run.  (In the context of a rule, the `command` variable is
-special and defines the command to run.  A full list of special
-variables is provided in <<ref_rule,the reference>>.)
-
-Within the context of a rule, three additional special variables are
-available: `$in` expands to the list of input files (`foo.c`) and
-`$out` to the output file (`foo.o`) for the command. For use with
-`$rspfile_content`, there is also `$in_newline`, which is the same as
-`$in`, except that multiple inputs are separated by `\n`, rather than
-spaces.
-
+command to run.  In the context of a rule, the `command` variable
+defines the command to run, `$in` expands to the list of
+input files (`foo.c`), and `$out` to the output files (`foo.o`) for the
+command.  A full list of special variables is provided in
+<<ref_rule,the reference>>.
 
 Build statements
 ~~~~~~~~~~~~~~~~
@@ -722,6 +716,20 @@ aborting due to a missing input.
   rebuilt if the command line changes; and secondly, they are not
   cleaned by default.
 
+`in`:: the shell-quoted space-separated list of files provided as
+  inputs to the build line referencing this `rule`.  (`$in` is provided
+  solely for convenience; if you need some subset or variant of this
+  list of files, just construct a new variable with that list and use
+  that instead.)
+
+`in_newline`:: the same as `$in` except that multiple inputs are
+  separated by newlines rather than spaces.  (For use with
+  `$rspfile_content`; this works around a bug in the MSVC linker where
+  it uses a fixed-size buffer for processing input.)
+
+`out`:: the shell-quoted space-separated list of files provided as
+  outputs to the build line referencing this `rule`.
+
 `restat`:: if present, causes Ninja to re-stat the command's outputs
   after execution of the command.  Each output whose modification time
   the command did not change will be treated as though it had never
@@ -748,10 +756,6 @@ rule link
 build myapp.exe: link a.obj b.obj [possibly many other .obj files]
 ----
 
-Finally, the special `$in` and `$out` variables expand to the
-shell-quoted space-separated list of files provided to the `build`
-line referencing this `rule`.
-
 [[ref_dependencies]]
 Build dependencies
 ~~~~~~~~~~~~~~~~~~
-- 
cgit v0.12


From 855f4eb641c1f5d5dc1414d537ae7b23b91e6537 Mon Sep 17 00:00:00 2001
From: Thiago Farina <tfarina@chromium.org>
Date: Tue, 19 Feb 2013 17:31:10 -0300
Subject: Removed unused UnitsWaiting() function from State class.

This function was added at 307f0bbd("and some basic implementation"), but nobody
calls it anymore.

Signed-off-by: Thiago Farina <tfarina@chromium.org>
---
 src/state.h | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/state.h b/src/state.h
index 326fbac..0a2e890 100644
--- a/src/state.h
+++ b/src/state.h
@@ -66,9 +66,7 @@ struct Pool {
   /// Dump the Pool and its edges (useful for debugging).
   void Dump() const;
 
-private:
-  int UnitsWaiting() { return delayed_.size(); }
-
+ private:
   string name_;
 
   /// |current_use_| is the total of the weights of the edges which are
-- 
cgit v0.12


From 1d6e670975bb31a483574457d05dd541b664bf77 Mon Sep 17 00:00:00 2001
From: Martin Olsson <martin@minimum.se>
Date: Tue, 19 Feb 2013 21:41:03 +0100
Subject: Fix typo, s/verison/version/

---
 doc/manual.asciidoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 1e5b70b..ac2326c 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -668,7 +668,7 @@ Two variables are significant when declared in the outermost file scope.
   discussion of the build log>>.  (You can also store other build output
   in this directory.)
 
-`ninja_required_version`:: the minimum verison of Ninja required to process
+`ninja_required_version`:: the minimum version of Ninja required to process
   the build correctly.  See <<ref_versioning,the discussion of versioning>>.
 
 
-- 
cgit v0.12


From 5f36d1047186d02905530c66ce6a21c5a245a813 Mon Sep 17 00:00:00 2001
From: Ben Vanik <ben.vanik@gmail.com>
Date: Tue, 19 Feb 2013 22:28:26 -0800
Subject: Add a check for the VC++ 2012-style 64-bit path. In VS2012 the path
 to the 64-bit tools has changed to VCINSTALLDIR\bin\x86_amd64\cl.exe. This
 change will make the bootstrap check there to see if it exists before falling
 back to the old amd64 path.

---
 bootstrap.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/bootstrap.py b/bootstrap.py
index 331ea0f..fcf1a20 100755
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -85,7 +85,9 @@ if options.windows:
 vcdir = os.environ.get('VCINSTALLDIR')
 if vcdir:
     if options.x64:
-        cl = [os.path.join(vcdir, 'bin', 'amd64', 'cl.exe')]
+        cl = [os.path.join(vcdir, 'bin', 'x86_amd64', 'cl.exe')]
+        if not os.path.exists(cl[0]):
+            cl = [os.path.join(vcdir, 'bin', 'amd64', 'cl.exe')]
     else:
         cl = [os.path.join(vcdir, 'bin', 'cl.exe')]
     args = cl + ['/nologo', '/EHsc', '/DNOMINMAX']
-- 
cgit v0.12


From 139800bf5d030769911099a705c53c1aa44ac6d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Peter=20K=C3=BCmmel?= <syntheticpp@gmx.net>
Date: Thu, 21 Feb 2013 19:42:12 +0100
Subject: Fix wrong usage of std::string::find

---
 src/version.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/version.cc b/src/version.cc
index 7e805c1..b3a93d1 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -26,7 +26,7 @@ void ParseVersion(const string& version, int* major, int* minor) {
   *minor = 0;
   if (end != string::npos) {
     size_t start = end + 1;
-    end = version.find(start, '.');
+    end = version.find('.', start);
     *minor = atoi(version.substr(start, end).c_str());
   }
 }
@@ -54,3 +54,4 @@ void CheckNinjaVersion(const string& version) {
 
 
 
+
-- 
cgit v0.12


From b7f11b3b495cb3d5964f9028dd3ea2c977565e65 Mon Sep 17 00:00:00 2001
From: Bei Zhang <ikarienator@chromium.org>
Date: Sat, 23 Feb 2013 22:51:21 -0800
Subject: Browse command does not parse URL correctly

---
 src/browse.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/browse.py b/src/browse.py
index 7f15e50..652bac2 100755
--- a/src/browse.py
+++ b/src/browse.py
@@ -29,6 +29,7 @@ except ImportError:
 import subprocess
 import sys
 import webbrowser
+import urllib2
 from collections import namedtuple
 
 Node = namedtuple('Node', ['inputs', 'rule', 'target', 'outputs'])
@@ -151,7 +152,7 @@ def ninja_dump(target):
 class RequestHandler(httpserver.BaseHTTPRequestHandler):
     def do_GET(self):
         assert self.path[0] == '/'
-        target = self.path[1:]
+        target = urllib2.unquote(self.path[1:])
 
         if target == '':
             self.send_response(302)
-- 
cgit v0.12


From bec60b9f1f561c69d5ff72398bbb5b21bd1e16ad Mon Sep 17 00:00:00 2001
From: Avinash Baliga <fifoforlifo@gmail.com>
Date: Sun, 3 Mar 2013 21:33:19 -0600
Subject: NINJA_STATUS now takes %e for elapsed time.

---
 src/build.cc | 8 ++++++++
 src/build.h  | 1 +
 2 files changed, 9 insertions(+)

diff --git a/src/build.cc b/src/build.cc
index 701fa92..2f5b267 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -238,6 +238,14 @@ string BuildStatus::FormatProgressStatus(
         out += buf;
         break;
 
+      case 'e':
+      {
+        double elapsed = overall_rate_.Elapsed();
+        snprintf(buf, sizeof(buf), "%.3fs", elapsed);
+        out += buf;
+        break;
+      }
+
       default:
         Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s);
         return "";
diff --git a/src/build.h b/src/build.h
index 23f653e..5747170 100644
--- a/src/build.h
+++ b/src/build.h
@@ -220,6 +220,7 @@ struct BuildStatus {
     RateInfo() : rate_(-1) {}
 
     void Restart() { stopwatch_.Restart(); }
+    double Elapsed() const { return stopwatch_.Elapsed(); }
     double rate() { return rate_; }
 
     void UpdateRate(int edges) {
-- 
cgit v0.12


From dce14e8e3dd42b2ef40f7b005f6a8696f6ad6a0a Mon Sep 17 00:00:00 2001
From: Avinash Baliga <fifoforlifo@gmail.com>
Date: Sun, 3 Mar 2013 22:04:48 -0600
Subject: Made %e purely a number.

---
 src/build.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/build.cc b/src/build.cc
index 2f5b267..74f5931 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -241,7 +241,7 @@ string BuildStatus::FormatProgressStatus(
       case 'e':
       {
         double elapsed = overall_rate_.Elapsed();
-        snprintf(buf, sizeof(buf), "%.3fs", elapsed);
+        snprintf(buf, sizeof(buf), "%.3f", elapsed);
         out += buf;
         break;
       }
-- 
cgit v0.12


From 4115b3f06efb67132948fba46794154c833e1f05 Mon Sep 17 00:00:00 2001
From: Dave Abrahams <dave@boostpro.com>
Date: Fri, 8 Mar 2013 13:18:12 -0800
Subject: Bring in declaration of atoi

./bootstrap.py fails on some platforms without this include
---
 src/ninja.cc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/ninja.cc b/src/ninja.cc
index 2b250b7..cc43325 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -15,6 +15,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-- 
cgit v0.12


From e99c493bc7a7d06cd0de91c3ac670315800e760b Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <peter@pcc.me.uk>
Date: Thu, 28 Feb 2013 00:28:23 -0800
Subject: Add compdb tool

This tool helps convert Ninja build files to a compilation database
of the form used by Clang tooling.
---
 doc/manual.asciidoc |  6 ++++++
 src/ninja.cc        | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 1e5b70b..ea26b8d 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -283,6 +283,12 @@ Files created but not referenced in the graph are not removed. This
 tool takes in account the +-v+ and the +-n+ options (note that +-n+
 implies +-v+).
 
+`compdb`:: given a list of rules, each of which is expected to be a
+C family language compiler rule whose first input is the name of the
+source file, prints on standard output a compilation database in the
+http://clang.llvm.org/docs/JSONCompilationDatabase.html[JSON format] expected
+by the Clang tooling interface.
+_Available since Ninja 1.2._
 
 
 Writing your own Ninja files
diff --git a/src/ninja.cc b/src/ninja.cc
index 2b250b7..c6a3500 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -485,6 +485,49 @@ int ToolClean(Globals* globals, int argc, char* argv[]) {
   }
 }
 
+void EncodeJSONString(const char *str) {
+  while (*str) {
+    if (*str == '"' || *str == '\\')
+      putchar('\\');
+    putchar(*str);
+    str++;
+  }
+}
+
+int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
+  bool first = true;
+  char cwd[PATH_MAX];
+
+  if (!getcwd(cwd, PATH_MAX)) {
+    Error("cannot determine working directory: %s", strerror(errno));
+    return 1;
+  }
+
+  putchar('[');
+  for (vector<Edge*>::iterator e = globals->state->edges_.begin();
+       e != globals->state->edges_.end(); ++e) {
+    for (int i = 0; i != argc; ++i) {
+      if ((*e)->rule_->name() == argv[i]) {
+        if (!first)
+          putchar(',');
+
+        printf("\n  {\n    \"directory\": \"");
+        EncodeJSONString(cwd);
+        printf("\",\n    \"command\": \"");
+        EncodeJSONString((*e)->EvaluateCommand().c_str());
+        printf("\",\n    \"file\": \"");
+        EncodeJSONString((*e)->inputs_[0]->path().c_str());
+        printf("\"\n  }");
+
+        first = false;
+      }
+    }
+  }
+
+  puts("\n]");
+  return 0;
+}
+
 int ToolUrtle(Globals* globals, int argc, char** argv) {
   // RLE encoded.
   const char* urtle =
@@ -533,6 +576,8 @@ int ChooseTool(const string& tool_name, const Tool** tool_out) {
       Tool::RUN_AFTER_LOAD, ToolQuery },
     { "targets",  "list targets by their rule or depth in the DAG",
       Tool::RUN_AFTER_LOAD, ToolTargets },
+    { "compdb",  "dump JSON compilation database to stdout",
+      Tool::RUN_AFTER_LOAD, ToolCompilationDatabase },
     { "urtle", NULL,
       Tool::RUN_AFTER_FLAGS, ToolUrtle },
     { NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL }
-- 
cgit v0.12


From 5c132de2b75642edd2f1ead0da11689fcbc9d819 Mon Sep 17 00:00:00 2001
From: Avinash Baliga <fifoforlifo@gmail.com>
Date: Mon, 11 Mar 2013 00:15:46 -0500
Subject: Added %e to manual.asciidoc, fixed brace style.

---
 doc/manual.asciidoc | 3 ++-
 src/build.cc        | 3 +--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 1e5b70b..5378d17 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -222,6 +222,7 @@ Several placeholders are available:
 `%f`:: The number of finished edges.
 `%o`:: Overall rate of finished edges per second
 `%c`:: Current rate of finished edges per second (average over builds specified by -j or its default)
+`%e`:: Elapsed time in seconds.  _(Available since Ninja 1.2.)_
 `%%`:: A plain `%` character.
 
 The default progress status is `"[%s/%t] "` (note the trailing space
@@ -684,7 +685,7 @@ keys.
   $variables are expanded) is passed directly to `sh -c` without
   interpretation by Ninja. Each `rule` may have only one `command`
   declaration. To specify multiple commands use `&&` (or similar) to
-  concatenate operations. 
+  concatenate operations.
 
 `depfile`:: path to an optional `Makefile` that contains extra
   _implicit dependencies_ (see <<ref_dependencies,the reference on
diff --git a/src/build.cc b/src/build.cc
index 74f5931..1e3ad9e 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -238,8 +238,7 @@ string BuildStatus::FormatProgressStatus(
         out += buf;
         break;
 
-      case 'e':
-      {
+      case 'e': {
         double elapsed = overall_rate_.Elapsed();
         snprintf(buf, sizeof(buf), "%.3f", elapsed);
         out += buf;
-- 
cgit v0.12


From ee8431a99d46c19d110853165ce9cbecae862ad0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Peter=20K=C3=BCmmel?= <syntheticpp@gmx.net>
Date: Thu, 14 Mar 2013 16:29:59 +0100
Subject: Posix symbols are not enabled by default for MSVC

---
 src/util.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/util.h b/src/util.h
index 2b59283..3c2a297 100644
--- a/src/util.h
+++ b/src/util.h
@@ -76,6 +76,8 @@ string ElideMiddle(const string& str, size_t width);
 #define unlink _unlink
 #define chdir _chdir
 #define strtoull _strtoui64
+#define getcwd _getcwd
+#define PATH_MAX _MAX_PATH
 #endif
 
 #ifdef _WIN32
-- 
cgit v0.12


From 72b3dae6aa936e9eabf4c22e5602c572a9756cbe Mon Sep 17 00:00:00 2001
From: Robert Iannucci <robbie@rail.com>
Date: Mon, 18 Mar 2013 09:53:53 -0700
Subject: Fix duplicate edge Pool crash in the minimally invasive way

---
 src/build.cc | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/build.cc b/src/build.cc
index 701fa92..2910385 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -418,6 +418,12 @@ Edge* Plan::FindWork() {
 void Plan::ScheduleWork(Edge* edge) {
   Pool* pool = edge->pool();
   if (pool->ShouldDelayEdge()) {
+    // The graph is not completely clean. Some Nodes have duplicate Out edges.
+    // We need to explicitly ignore these here, otherwise their work will get
+    // scheduled twice (see https://github.com/martine/ninja/pull/519)
+    if (ready_.count(edge)) {
+      return;
+    }
     pool->DelayEdge(edge);
     pool->RetrieveReadyEdges(&ready_);
   } else {
-- 
cgit v0.12


From 8e70a53058a74d3582c609fd9f7c133dae7387b4 Mon Sep 17 00:00:00 2001
From: Robert Iannucci <robbie@rail.com>
Date: Mon, 18 Mar 2013 10:36:23 -0700
Subject: Fix Pool to use a set internally

---
 src/state.cc | 19 ++++++++++++++-----
 src/state.h  |  6 ++++--
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/src/state.cc b/src/state.cc
index b5d2622..cd43e0d 100644
--- a/src/state.cc
+++ b/src/state.cc
@@ -35,23 +35,25 @@ void Pool::EdgeFinished(const Edge& edge) {
 
 void Pool::DelayEdge(Edge* edge) {
   assert(depth_ != 0);
-  delayed_.push_back(edge);
+  delayed_.insert(edge);
 }
 
 void Pool::RetrieveReadyEdges(set<Edge*>* ready_queue) {
-  while (!delayed_.empty()) {
-    Edge* edge = delayed_.front();
+  set<Edge*>::iterator it = delayed_.begin();
+  while (it != delayed_.end()) {
+    Edge* edge = *it;
     if (current_use_ + edge->weight() > depth_)
       break;
-    delayed_.pop_front();
     ready_queue->insert(edge);
     EdgeScheduled(*edge);
+    ++it;
   }
+  delayed_.erase(delayed_.begin(), it);
 }
 
 void Pool::Dump() const {
   printf("%s (%d/%d) ->\n", name_.c_str(), current_use_, depth_);
-  for (deque<Edge*>::const_iterator it = delayed_.begin();
+  for (set<Edge*>::const_iterator it = delayed_.begin();
        it != delayed_.end(); ++it)
   {
     printf("\t");
@@ -59,6 +61,13 @@ void Pool::Dump() const {
   }
 }
 
+bool Pool::WeightedEdgeCmp(const Edge* a, const Edge* b) {
+  if (!a) return b;
+  if (!b) return false;
+  int weight_diff = a->weight() - b->weight();
+  return ((weight_diff < 0) || (weight_diff == 0 && a < b));
+}
+
 Pool State::kDefaultPool("", 0);
 const Rule State::kPhonyRule("phony");
 
diff --git a/src/state.h b/src/state.h
index 0a2e890..279a64a 100644
--- a/src/state.h
+++ b/src/state.h
@@ -39,7 +39,7 @@ struct Rule;
 /// completes).
 struct Pool {
   explicit Pool(const string& name, int depth)
-    : name_(name), current_use_(0), depth_(depth) { }
+    : name_(name), current_use_(0), depth_(depth), delayed_(&WeightedEdgeCmp) { }
 
   // A depth of 0 is infinite
   bool is_valid() const { return depth_ >= 0; }
@@ -74,7 +74,9 @@ struct Pool {
   int current_use_;
   int depth_;
 
-  deque<Edge*> delayed_;
+  static bool WeightedEdgeCmp(const Edge* a, const Edge* b);
+
+  set<Edge*,bool(*)(const Edge*, const Edge*)> delayed_;
 };
 
 /// Global state (file status, loaded rules) for a single run.
-- 
cgit v0.12


From f31836a18621a5477ae6888d832afb96f1a56f52 Mon Sep 17 00:00:00 2001
From: Robert Iannucci <robbie@rail.com>
Date: Sat, 23 Mar 2013 14:31:05 -0700
Subject: Fix debug build on linux (type strictness).

---
 src/state.cc | 4 ++--
 src/state.h  | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/state.cc b/src/state.cc
index cd43e0d..9f46fee 100644
--- a/src/state.cc
+++ b/src/state.cc
@@ -39,7 +39,7 @@ void Pool::DelayEdge(Edge* edge) {
 }
 
 void Pool::RetrieveReadyEdges(set<Edge*>* ready_queue) {
-  set<Edge*>::iterator it = delayed_.begin();
+  DelayedEdges::iterator it = delayed_.begin();
   while (it != delayed_.end()) {
     Edge* edge = *it;
     if (current_use_ + edge->weight() > depth_)
@@ -53,7 +53,7 @@ void Pool::RetrieveReadyEdges(set<Edge*>* ready_queue) {
 
 void Pool::Dump() const {
   printf("%s (%d/%d) ->\n", name_.c_str(), current_use_, depth_);
-  for (set<Edge*>::const_iterator it = delayed_.begin();
+  for (DelayedEdges::const_iterator it = delayed_.begin();
        it != delayed_.end(); ++it)
   {
     printf("\t");
diff --git a/src/state.h b/src/state.h
index 279a64a..7e3aead 100644
--- a/src/state.h
+++ b/src/state.h
@@ -76,7 +76,8 @@ struct Pool {
 
   static bool WeightedEdgeCmp(const Edge* a, const Edge* b);
 
-  set<Edge*,bool(*)(const Edge*, const Edge*)> delayed_;
+  typedef set<Edge*,bool(*)(const Edge*, const Edge*)> DelayedEdges;
+  DelayedEdges delayed_;
 };
 
 /// Global state (file status, loaded rules) for a single run.
-- 
cgit v0.12


From 55be532e10777b050da68e3baede42181202558c Mon Sep 17 00:00:00 2001
From: Robert Iannucci <robbie@rail.com>
Date: Sat, 23 Mar 2013 14:35:10 -0700
Subject: Add regression test

---
 src/build_test.cc | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/src/build_test.cc b/src/build_test.cc
index 59c4c53..40a82ed 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -302,6 +302,80 @@ TEST_F(PlanTest, PoolsWithDepthTwo) {
   ASSERT_FALSE(plan_.FindWork());
 }
 
+TEST_F(PlanTest, PoolWithRedundantEdges) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+    "pool compile\n"
+    "  depth = 1\n"
+    "rule gen_foo\n"
+    "  command = touch foo.cpp\n"
+    "rule gen_bar\n"
+    "  command = touch bar.cpp\n"
+    "rule echo\n"
+    "  command = echo $out > $out\n"
+    "build foo.cpp.obj: echo foo.cpp || foo.cpp\n"
+    "  pool = compile\n"
+    "build bar.cpp.obj: echo bar.cpp || bar.cpp\n"
+    "  pool = compile\n"
+    "build libfoo.a: echo foo.cpp.obj bar.cpp.obj\n"
+    "build foo.cpp: gen_foo\n"
+    "build bar.cpp: gen_bar\n"
+    "build all: phony libfoo.a\n"));
+  GetNode("foo.cpp")->MarkDirty();
+  GetNode("foo.cpp.obj")->MarkDirty();
+  GetNode("bar.cpp")->MarkDirty();
+  GetNode("bar.cpp.obj")->MarkDirty();
+  GetNode("libfoo.a")->MarkDirty();
+  GetNode("all")->MarkDirty();
+  string err;
+  EXPECT_TRUE(plan_.AddTarget(GetNode("all"), &err));
+  ASSERT_EQ("", err);
+  ASSERT_TRUE(plan_.more_to_do());
+
+  Edge* edge = NULL;
+
+  edge = plan_.FindWork();
+  ASSERT_TRUE(edge);
+  ASSERT_EQ("foo.cpp", edge->outputs_[0]->path());
+  plan_.EdgeFinished(edge);
+
+  edge = plan_.FindWork();
+  ASSERT_TRUE(edge);
+  ASSERT_EQ("foo.cpp", edge->inputs_[0]->path());
+  ASSERT_EQ("foo.cpp", edge->inputs_[1]->path());
+  ASSERT_EQ("foo.cpp.obj", edge->outputs_[0]->path());
+  plan_.EdgeFinished(edge);
+
+  edge = plan_.FindWork();
+  ASSERT_TRUE(edge);
+  ASSERT_EQ("bar.cpp", edge->outputs_[0]->path());
+  plan_.EdgeFinished(edge);
+
+  edge = plan_.FindWork();
+  ASSERT_TRUE(edge);
+  ASSERT_EQ("bar.cpp", edge->inputs_[0]->path());
+  ASSERT_EQ("bar.cpp", edge->inputs_[1]->path());
+  ASSERT_EQ("bar.cpp.obj", edge->outputs_[0]->path());
+  plan_.EdgeFinished(edge);
+
+  edge = plan_.FindWork();
+  ASSERT_TRUE(edge);
+  ASSERT_EQ("foo.cpp.obj", edge->inputs_[0]->path());
+  ASSERT_EQ("bar.cpp.obj", edge->inputs_[1]->path());
+  ASSERT_EQ("libfoo.a", edge->outputs_[0]->path());
+  plan_.EdgeFinished(edge);
+
+  edge = plan_.FindWork();
+  ASSERT_TRUE(edge);
+  ASSERT_EQ("libfoo.a", edge->inputs_[0]->path());
+  ASSERT_EQ("all", edge->outputs_[0]->path());
+  plan_.EdgeFinished(edge);
+
+  edge = plan_.FindWork();
+  ASSERT_FALSE(edge);
+  ASSERT_FALSE(plan_.more_to_do());
+}
+
+
 struct BuildTest : public StateTestWithBuiltinRules,
                    public CommandRunner {
   BuildTest() : config_(MakeConfig()),
-- 
cgit v0.12


From 61f4a9905a7e68149a5420617f23d4e73783335c Mon Sep 17 00:00:00 2001
From: Nico Weber <thakis@chromium.org>
Date: Wed, 27 Mar 2013 16:03:52 -0700
Subject: Add spell checking for debug flags.

I just used `ninja -d stat` and it took me a bit to realize that I missed the
trailing 's'.

While here, move the message printing from printf() to Error(). This makes the
output consistent with other error outputs: The messages are now prefixed with
"ninja: error: " instead of just "ninja: " and they go to stderr instead of
stdout.
---
 src/ninja.cc | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/ninja.cc b/src/ninja.cc
index b24ac33..69646e1 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -629,7 +629,14 @@ bool DebugEnable(const string& name, Globals* globals) {
     g_explaining = true;
     return true;
   } else {
-    printf("ninja: unknown debug setting '%s'\n", name.c_str());
+    const char* suggestion =
+        SpellcheckString(name, "stats", "explain", NULL);
+    if (suggestion) {
+      Error("unknown debug setting '%s', did you mean '%s'?",
+            name.c_str(), suggestion);
+    } else {
+      Error("unknown debug setting '%s'", name.c_str());
+    }
     return false;
   }
 }
-- 
cgit v0.12


From 7ab6dcbdb6447861eefafc47fc3e10f3273cede2 Mon Sep 17 00:00:00 2001
From: Evan Martin <martine@danga.com>
Date: Sat, 30 Mar 2013 16:36:47 -0700
Subject: allow paths with '!' in depfiles

See funny paths in
  https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/
---
 src/depfile_parser.cc      | 44 ++++++++++++++++----------------------------
 src/depfile_parser.in.cc   |  2 +-
 src/depfile_parser_test.cc |  6 ++++--
 3 files changed, 21 insertions(+), 31 deletions(-)

diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc
index 6887c91..c8fb92e 100644
--- a/src/depfile_parser.cc
+++ b/src/depfile_parser.cc
@@ -53,10 +53,10 @@ bool DepfileParser::Parse(string* content, string* err) {
           0,   0,   0,   0,   0,   0,   0,   0, 
           0,   0,   0,   0,   0,   0,   0,   0, 
           0,   0,   0,   0,   0,   0,   0,   0, 
-          0,   0,   0,   0,   0,   0,   0,   0, 
-        128, 128,   0, 128, 128, 128, 128, 128, 
+          0, 128, 128, 128, 128, 128, 128, 128, 
         128, 128, 128, 128, 128, 128, 128, 128, 
-        128, 128, 128,   0,   0, 128,   0,   0, 
+        128, 128, 128, 128, 128, 128, 128, 128, 
+        128, 128, 128, 128, 128, 128,   0,   0, 
         128, 128, 128, 128, 128, 128, 128, 128, 
         128, 128, 128, 128, 128, 128, 128, 128, 
         128, 128, 128, 128, 128, 128, 128, 128, 
@@ -84,38 +84,26 @@ bool DepfileParser::Parse(string* content, string* err) {
       };
 
       yych = *in;
-      if (yych <= 'Z') {
-        if (yych <= '*') {
+      if (yych <= '\\') {
+        if (yych <= '=') {
           if (yych <= 0x00) goto yy6;
-          if (yych <= '\'') goto yy8;
-          if (yych <= ')') goto yy4;
-          goto yy8;
+          if (yych <= ' ') goto yy8;
+          goto yy4;
         } else {
-          if (yych <= '<') {
-            if (yych <= ':') goto yy4;
-            goto yy8;
-          } else {
-            if (yych <= '=') goto yy4;
-            if (yych <= '?') goto yy8;
-            goto yy4;
-          }
+          if (yych <= '?') goto yy8;
+          if (yych <= 'Z') goto yy4;
+          if (yych <= '[') goto yy8;
         }
       } else {
-        if (yych <= '_') {
-          if (yych == '\\') goto yy2;
-          if (yych <= '^') goto yy8;
-          goto yy4;
+        if (yych <= '`') {
+          if (yych == '_') goto yy4;
+          goto yy8;
         } else {
-          if (yych <= 'z') {
-            if (yych <= '`') goto yy8;
-            goto yy4;
-          } else {
-            if (yych == '~') goto yy4;
-            goto yy8;
-          }
+          if (yych <= 'z') goto yy4;
+          if (yych == '~') goto yy4;
+          goto yy8;
         }
       }
-yy2:
       ++in;
       if ((yych = *in) <= '$') {
         if (yych <= '\n') {
diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc
index 1d4a177..f96cdb3 100644
--- a/src/depfile_parser.in.cc
+++ b/src/depfile_parser.in.cc
@@ -68,7 +68,7 @@ bool DepfileParser::Parse(string* content, string* err) {
         *out++ = yych;
         continue;
       }
-      [a-zA-Z0-9+,/_:.~()@=-]+ {
+      [a-zA-Z0-9+,/_:.~()@=-!]+ {
         // Got a span of plain text.
         int len = (int)(in - start);
         // Need to shift it over if we're overwriting backslashes.
diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc
index 93d42db..552975c 100644
--- a/src/depfile_parser_test.cc
+++ b/src/depfile_parser_test.cc
@@ -104,10 +104,12 @@ TEST_F(DepfileParserTest, Escapes) {
 }
 
 TEST_F(DepfileParserTest, SpecialChars) {
+  // See filenames like istreambuf.iterator_op!= in
+  // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/
   string err;
   EXPECT_TRUE(Parse(
 "C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n"
-" en@quot.header~ t+t-x=1",
+" en@quot.header~ t+t-x!=1",
       &err));
   ASSERT_EQ("", err);
   EXPECT_EQ("C:/Program Files (x86)/Microsoft crtdefs.h",
@@ -115,7 +117,7 @@ TEST_F(DepfileParserTest, SpecialChars) {
   ASSERT_EQ(2u, parser_.ins_.size());
   EXPECT_EQ("en@quot.header~",
             parser_.ins_[0].AsString());
-  EXPECT_EQ("t+t-x=1",
+  EXPECT_EQ("t+t-x!=1",
             parser_.ins_[1].AsString());
 }
 
-- 
cgit v0.12


From e2701da32d223f2ce165f2e59c2c396da80debc6 Mon Sep 17 00:00:00 2001
From: Nico Weber <nicolasweber@gmx.de>
Date: Tue, 2 Apr 2013 09:39:05 -0700
Subject: Dollar signs in depfiles are escaped as "$$", not "\$".

See http://llvm.org/PR15642. I checked that gcc does produce depfiles
containing "$$" for files with "$" signs in their name (and as of
r178540, so does clang). I also checked that .d files that escape dollar
signs with "\$" are not read correctly by make.
---
 src/depfile_parser.cc      | 77 ++++++++++++++++++++++++++++------------------
 src/depfile_parser.in.cc   |  7 ++++-
 src/depfile_parser_test.cc |  2 +-
 3 files changed, 54 insertions(+), 32 deletions(-)

diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc
index c8fb92e..5a30c6b 100644
--- a/src/depfile_parser.cc
+++ b/src/depfile_parser.cc
@@ -84,44 +84,48 @@ bool DepfileParser::Parse(string* content, string* err) {
       };
 
       yych = *in;
-      if (yych <= '\\') {
-        if (yych <= '=') {
-          if (yych <= 0x00) goto yy6;
-          if (yych <= ' ') goto yy8;
+      if (yych <= '[') {
+        if (yych <= '$') {
+          if (yych <= 0x00) goto yy7;
+          if (yych <= ' ') goto yy9;
+          if (yych <= '#') goto yy6;
           goto yy4;
         } else {
-          if (yych <= '?') goto yy8;
-          if (yych <= 'Z') goto yy4;
-          if (yych <= '[') goto yy8;
+          if (yych <= '=') goto yy6;
+          if (yych <= '?') goto yy9;
+          if (yych <= 'Z') goto yy6;
+          goto yy9;
         }
       } else {
         if (yych <= '`') {
-          if (yych == '_') goto yy4;
-          goto yy8;
+          if (yych <= '\\') goto yy2;
+          if (yych == '_') goto yy6;
+          goto yy9;
         } else {
-          if (yych <= 'z') goto yy4;
-          if (yych == '~') goto yy4;
-          goto yy8;
+          if (yych <= 'z') goto yy6;
+          if (yych == '~') goto yy6;
+          goto yy9;
         }
       }
+yy2:
       ++in;
-      if ((yych = *in) <= '$') {
+      if ((yych = *in) <= '#') {
         if (yych <= '\n') {
           if (yych <= 0x00) goto yy3;
-          if (yych <= '\t') goto yy11;
+          if (yych <= '\t') goto yy14;
         } else {
-          if (yych == ' ') goto yy13;
-          if (yych <= '"') goto yy11;
-          goto yy13;
+          if (yych == ' ') goto yy16;
+          if (yych <= '"') goto yy14;
+          goto yy16;
         }
       } else {
         if (yych <= 'Z') {
-          if (yych == '*') goto yy13;
-          goto yy11;
+          if (yych == '*') goto yy16;
+          goto yy14;
         } else {
-          if (yych <= '\\') goto yy13;
-          if (yych == '|') goto yy13;
-          goto yy11;
+          if (yych <= '\\') goto yy16;
+          if (yych == '|') goto yy16;
+          goto yy14;
         }
       }
 yy3:
@@ -132,8 +136,8 @@ yy3:
       }
 yy4:
       ++in;
-      yych = *in;
-      goto yy10;
+      if ((yych = *in) == '$') goto yy12;
+      goto yy11;
 yy5:
       {
         // Got a span of plain text.
@@ -145,22 +149,35 @@ yy5:
         continue;
       }
 yy6:
+      yych = *++in;
+      goto yy11;
+yy7:
       ++in;
       {
         break;
       }
-yy8:
+yy9:
       yych = *++in;
       goto yy3;
-yy9:
+yy10:
       ++in;
       yych = *in;
-yy10:
+yy11:
       if (yybm[0+yych] & 128) {
-        goto yy9;
+        goto yy10;
       }
       goto yy5;
-yy11:
+yy12:
+      ++in;
+      if (yybm[0+(yych = *in)] & 128) {
+        goto yy10;
+      }
+      {
+        // De-escape dollar character.
+        *out++ = '$';
+        continue;
+      }
+yy14:
       ++in;
       {
         // Let backslash before other characters through verbatim.
@@ -168,7 +185,7 @@ yy11:
         *out++ = yych;
         continue;
       }
-yy13:
+yy16:
       ++in;
       {
         // De-escape backslashed character.
diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc
index f96cdb3..cf24a09 100644
--- a/src/depfile_parser.in.cc
+++ b/src/depfile_parser.in.cc
@@ -55,13 +55,18 @@ bool DepfileParser::Parse(string* content, string* err) {
       re2c:indent:string = "  ";
 
       nul = "\000";
-      escape = [ \\#*$[|];
+      escape = [ \\#*[|];
 
       '\\' escape {
         // De-escape backslashed character.
         *out++ = yych;
         continue;
       }
+      '$$' {
+        // De-escape dollar character.
+        *out++ = '$';
+        continue;
+      }
       '\\' [^\000\n] {
         // Let backslash before other characters through verbatim.
         *out++ = '\\';
diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc
index 552975c..0f6771a 100644
--- a/src/depfile_parser_test.cc
+++ b/src/depfile_parser_test.cc
@@ -95,7 +95,7 @@ TEST_F(DepfileParserTest, Escapes) {
   // it through.
   string err;
   EXPECT_TRUE(Parse(
-"\\!\\@\\#\\$\\%\\^\\&\\\\",
+"\\!\\@\\#$$\\%\\^\\&\\\\",
       &err));
   ASSERT_EQ("", err);
   EXPECT_EQ("\\!\\@#$\\%\\^\\&\\",
-- 
cgit v0.12