basic let statement
authorEvan Martin <martine@danga.com>
Wed, 20 Oct 2010 03:48:30 +0000 (20:48 -0700)
committerEvan Martin <martine@danga.com>
Wed, 20 Oct 2010 03:54:37 +0000 (20:54 -0700)
manifest_parser.h
ninja.h
ninja_test.cc

index 0b2eff2..e9fc40b 100644 (file)
@@ -10,6 +10,7 @@ struct ManifestParser {
   bool Error(const string& message, string* err);
 
   bool ParseRule(string* err);
+  bool ParseLet(string* err);
   bool ParseEdge(string* err);
 
   bool SkipWhitespace(bool newline=false);
@@ -55,6 +56,9 @@ bool ManifestParser::Parse(const string& input, string* err) {
     if (token_ == "rule") {
       if (!ParseRule(err))
         return false;
+    } else if (token_ == "let") {
+      if (!ParseLet(err))
+        return false;
     } else if (token_ == "build") {
       if (!ParseEdge(err))
         return false;
@@ -97,6 +101,28 @@ bool ManifestParser::ParseRule(string* err) {
   return true;
 }
 
+bool ManifestParser::ParseLet(string* err) {
+  SkipWhitespace();
+  if (!NextToken())
+    return Error("expected variable name", err);
+  string name = token_;
+
+  SkipWhitespace();
+
+  if (!NextToken() || token_ != "=")
+    return Error("expected =", err);
+
+  SkipWhitespace();
+
+  string value;
+  if (!ReadToNewline(&value, err))
+    return false;
+
+  state_->AddBinding(name, value);
+
+  return true;
+}
+
 bool ManifestParser::ParseEdge(string* err) {
   string rule;
   vector<string> ins, outs;
@@ -177,8 +203,8 @@ bool ManifestParser::NextToken() {
       token_.push_back(*cur_);
       ++col_; ++cur_;
     }
-  } else if (*cur_ == ':') {
-    token_ = ":";
+  } else if (*cur_ == ':' || *cur_ == '=') {
+    token_ = *cur_;
     ++col_; ++cur_;
   }
 
diff --git a/ninja.h b/ninja.h
index 24466b5..f5a686d 100644 (file)
--- a/ninja.h
+++ b/ninja.h
@@ -101,7 +101,7 @@ struct Rule {
 };
 
 struct Edge {
-  Edge() : rule_(NULL) {}
+  Edge() : rule_(NULL), env_(NULL) {}
 
   void MarkDirty(Node* node);
   void RecomputeDirty();
@@ -111,6 +111,7 @@ struct Edge {
   enum InOut { IN, OUT };
   vector<Node*> inputs_;
   vector<Node*> outputs_;
+  EvalString::Env* env_;
 };
 
 void FileStat::Touch(int mtime) {
@@ -186,6 +187,8 @@ struct EdgeEnv : public EvalString::Env {
       }
     } else if (var == "$out") {
       result = edge_->outputs_[0]->file_->path_;
+    } else if (edge_->env_) {
+      return edge_->env_->Evaluate(var);
     }
     return result;
   }
@@ -242,20 +245,34 @@ void StatCache::Reload() {
   }
 }
 
-struct State {
+struct State : public EvalString::Env {
   StatCache stat_cache_;
   map<string, Rule*> rules_;
   vector<Edge*> edges_;
+  map<string, string> env_;
 
   StatCache* stat_cache() { return &stat_cache_; }
 
+  // EvalString::Env impl
+  virtual string Evaluate(const string& var);
+
   Rule* AddRule(const string& name, const string& command);
   Edge* AddEdge(Rule* rule);
   Edge* AddEdge(const string& rule_name);
   Node* GetNode(const string& path);
   void AddInOut(Edge* edge, Edge::InOut inout, const string& path);
+  void AddBinding(const string& key, const string& val);
 };
 
+string State::Evaluate(const string& var) {
+  if (var.size() > 1 && var[0] == '$') {
+    map<string, string>::iterator i = env_.find(var.substr(1));
+    if (i != env_.end())
+      return i->second;
+  }
+  return "";
+}
+
 Rule* State::AddRule(const string& name, const string& command) {
   Rule* rule = new Rule(name, command);
   rules_[name] = rule;
@@ -269,6 +286,7 @@ Edge* State::AddEdge(const string& rule_name) {
 Edge* State::AddEdge(Rule* rule) {
   Edge* edge = new Edge();
   edge->rule_ = rule;
+  edge->env_ = this;
   edges_.push_back(edge);
   return edge;
 }
@@ -292,6 +310,10 @@ void State::AddInOut(Edge* edge, Edge::InOut inout, const string& path) {
   }
 }
 
+void State::AddBinding(const string& key, const string& val) {
+  env_[key] = val;
+}
+
 struct Plan {
   explicit Plan(State* state) : state_(state) {}
 
index dc03e65..e298c4f 100644 (file)
@@ -31,6 +31,24 @@ TEST(Parser, Rules) {
   EXPECT_EQ("cat @in > $out", rule->command_.unparsed());
 }
 
+TEST(Parser, Variables) {
+  State state;
+  ManifestParser parser(&state);
+  string err;
+  EXPECT_TRUE(parser.Parse(
+"rule link\n"
+"command ld $extra -o $out @in\n"
+"\n"
+"let extra = -pthread\n"
+"build a: link b c\n",
+      &err));
+  EXPECT_EQ("", err);
+
+  ASSERT_EQ(1, state.edges_.size());
+  Edge* edge = state.edges_[0];
+  EXPECT_EQ("ld -pthread -o a b c", edge->EvaluateCommand());
+}
+
 TEST(State, Basic) {
   State state;
   Rule* rule = state.AddRule("cat", "cat @in > $out");