Permalink
Browse files

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.
  • Loading branch information...
evmar committed Oct 27, 2012
1 parent 3249938 commit 13dd08c1a03e5a8f4299816fbd3af1b6cb6d9642
Showing with 183 additions and 154 deletions.
  1. +20 −18 src/build.cc
  2. +3 −3 src/clean.cc
  3. +16 −0 src/eval_env.cc
  4. +12 −0 src/eval_env.h
  5. +45 −36 src/graph.cc
  6. +10 −33 src/graph.h
  7. +35 −0 src/graph_test.cc
  8. +25 −51 src/manifest_parser.cc
  9. +12 −8 src/manifest_parser_test.cc
  10. +2 −2 src/state.cc
  11. +1 −1 src/state.h
  12. +2 −2 src/state_test.cc
View
@@ -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);
}
View
@@ -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) {
View
@@ -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) {
View
@@ -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_;
View
@@ -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);
View
@@ -102,38 +102,23 @@ struct Node {
/// 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,25 +138,17 @@ 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;
const Rule* rule_;
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_;
Oops, something went wrong.

0 comments on commit 13dd08c

Please sign in to comment.