bool Error(const string& message, string* err);
bool ParseRule(string* err);
+ bool ParseLet(string* err);
bool ParseEdge(string* err);
bool SkipWhitespace(bool newline=false);
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;
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;
token_.push_back(*cur_);
++col_; ++cur_;
}
- } else if (*cur_ == ':') {
- token_ = ":";
+ } else if (*cur_ == ':' || *cur_ == '=') {
+ token_ = *cur_;
++col_; ++cur_;
}
};
struct Edge {
- Edge() : rule_(NULL) {}
+ Edge() : rule_(NULL), env_(NULL) {}
void MarkDirty(Node* node);
void RecomputeDirty();
enum InOut { IN, OUT };
vector<Node*> inputs_;
vector<Node*> outputs_;
+ EvalString::Env* env_;
};
void FileStat::Touch(int mtime) {
}
} else if (var == "$out") {
result = edge_->outputs_[0]->file_->path_;
+ } else if (edge_->env_) {
+ return edge_->env_->Evaluate(var);
}
return result;
}
}
}
-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;
Edge* State::AddEdge(Rule* rule) {
Edge* edge = new Edge();
edge->rule_ = rule;
+ edge->env_ = this;
edges_.push_back(edge);
return edge;
}
}
}
+void State::AddBinding(const string& key, const string& val) {
+ env_[key] = val;
+}
+
struct Plan {
explicit Plan(State* state) : state_(state) {}
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");