2010-10-14 23:01:22 -07:00
|
|
|
#include <algorithm>
|
2010-10-14 22:14:06 -07:00
|
|
|
#include <map>
|
2010-10-14 23:40:26 -07:00
|
|
|
#include <queue>
|
|
|
|
#include <set>
|
2010-10-14 22:14:06 -07:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2010-10-14 23:22:53 -07:00
|
|
|
#include <assert.h>
|
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
struct Node;
|
|
|
|
struct FileStat {
|
2010-10-14 23:01:22 -07:00
|
|
|
FileStat(const string& path) : path_(path), mtime_(0), node_(NULL) {}
|
|
|
|
void Touch(int mtime);
|
2010-10-18 11:58:03 -07:00
|
|
|
void Stat();
|
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
string path_;
|
2010-10-14 23:01:22 -07:00
|
|
|
int mtime_;
|
2010-10-14 22:14:06 -07:00
|
|
|
Node* node_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Edge;
|
|
|
|
struct Node {
|
2010-10-14 23:39:50 -07:00
|
|
|
Node(FileStat* file) : file_(file), dirty_(false), in_edge_(NULL) {}
|
2010-10-14 23:01:22 -07:00
|
|
|
|
|
|
|
bool dirty() const { return dirty_; }
|
|
|
|
void MarkDirty();
|
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
FileStat* file_;
|
|
|
|
bool dirty_;
|
2010-10-14 23:22:53 -07:00
|
|
|
Edge* in_edge_;
|
|
|
|
vector<Edge*> out_edges_;
|
2010-10-14 22:14:06 -07:00
|
|
|
};
|
|
|
|
|
2010-10-15 00:10:49 -07:00
|
|
|
struct EvalString {
|
|
|
|
struct Env {
|
|
|
|
virtual string Evaluate(const string& var) = 0;
|
|
|
|
};
|
|
|
|
bool Parse(const string& input);
|
|
|
|
string Evaluate(Env* env);
|
|
|
|
|
2010-10-15 18:08:10 -07:00
|
|
|
const string& unparsed() const { return unparsed_; }
|
|
|
|
|
|
|
|
string unparsed_;
|
2010-10-15 00:10:49 -07:00
|
|
|
enum TokenType { RAW, SPECIAL };
|
|
|
|
typedef vector<pair<string, TokenType> > TokenList;
|
|
|
|
TokenList parsed_;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool EvalString::Parse(const string& input) {
|
2010-10-15 18:08:10 -07:00
|
|
|
unparsed_ = input;
|
|
|
|
|
2010-10-15 00:10:49 -07:00
|
|
|
string::size_type start, end;
|
|
|
|
start = 0;
|
|
|
|
do {
|
|
|
|
end = input.find_first_of("@$", start);
|
|
|
|
if (end == string::npos) {
|
|
|
|
end = input.size();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (end > start)
|
|
|
|
parsed_.push_back(make_pair(input.substr(start, end - start), RAW));
|
|
|
|
start = end;
|
|
|
|
for (end = start + 1; end < input.size(); ++end) {
|
|
|
|
if (!('a' <= input[end] && input[end] <= 'z'))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (end == start + 1) {
|
|
|
|
// XXX report bad parse here
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
parsed_.push_back(make_pair(input.substr(start, end - start), SPECIAL));
|
|
|
|
start = end;
|
|
|
|
} while (end < input.size());
|
|
|
|
if (end > start)
|
|
|
|
parsed_.push_back(make_pair(input.substr(start, end - start), RAW));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
string EvalString::Evaluate(Env* env) {
|
|
|
|
string result;
|
|
|
|
for (TokenList::iterator i = parsed_.begin(); i != parsed_.end(); ++i) {
|
|
|
|
if (i->second == RAW)
|
|
|
|
result.append(i->first);
|
|
|
|
else
|
|
|
|
result.append(env->Evaluate(i->first));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
struct Rule {
|
|
|
|
Rule(const string& name, const string& command) :
|
2010-10-15 00:15:42 -07:00
|
|
|
name_(name) {
|
|
|
|
assert(command_.Parse(command)); // XXX
|
|
|
|
}
|
2010-10-14 22:14:06 -07:00
|
|
|
string name_;
|
2010-10-15 00:15:42 -07:00
|
|
|
EvalString command_;
|
2010-10-14 22:14:06 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Edge {
|
2010-10-14 23:01:22 -07:00
|
|
|
Edge() : rule_(NULL) {}
|
|
|
|
|
|
|
|
void MarkDirty(Node* node);
|
2010-10-14 23:45:57 -07:00
|
|
|
string EvaluateCommand(); // XXX move to env, take env ptr
|
2010-10-14 23:01:22 -07:00
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
Rule* rule_;
|
|
|
|
enum InOut { IN, OUT };
|
|
|
|
vector<Node*> inputs_;
|
|
|
|
vector<Node*> outputs_;
|
|
|
|
};
|
|
|
|
|
2010-10-14 23:01:22 -07:00
|
|
|
void FileStat::Touch(int mtime) {
|
|
|
|
if (node_)
|
|
|
|
node_->MarkDirty();
|
|
|
|
}
|
|
|
|
|
2010-10-18 11:58:03 -07:00
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
void FileStat::Stat() {
|
|
|
|
struct stat st;
|
|
|
|
if (stat(path_.c_str(), &st) < 0) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
mtime_ = 0;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "stat(%s): %s\n", path_.c_str(), strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mtime_ = st.st_mtime;
|
|
|
|
}
|
|
|
|
|
2010-10-14 23:01:22 -07:00
|
|
|
void Node::MarkDirty() {
|
|
|
|
if (dirty_)
|
|
|
|
return; // We already know.
|
2010-10-17 18:15:14 -07:00
|
|
|
|
|
|
|
if (in_edge_) // No input edges means never dirty.
|
|
|
|
dirty_ = true;
|
2010-10-14 23:22:53 -07:00
|
|
|
for (vector<Edge*>::iterator i = out_edges_.begin(); i != out_edges_.end(); ++i)
|
2010-10-14 23:01:22 -07:00
|
|
|
(*i)->MarkDirty(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Edge::MarkDirty(Node* node) {
|
|
|
|
vector<Node*>::iterator i = find(inputs_.begin(), inputs_.end(), node);
|
|
|
|
if (i == inputs_.end())
|
|
|
|
return;
|
|
|
|
for (i = outputs_.begin(); i != outputs_.end(); ++i)
|
|
|
|
(*i)->MarkDirty();
|
|
|
|
}
|
|
|
|
|
2010-10-15 00:15:42 -07:00
|
|
|
struct EdgeEnv : public EvalString::Env {
|
|
|
|
EdgeEnv(Edge* edge) : edge_(edge) {}
|
|
|
|
virtual string Evaluate(const string& var) {
|
|
|
|
string result;
|
|
|
|
if (var == "@in") {
|
|
|
|
for (vector<Node*>::iterator i = edge_->inputs_.begin();
|
|
|
|
i != edge_->inputs_.end(); ++i) {
|
|
|
|
if (!result.empty())
|
|
|
|
result.push_back(' ');
|
|
|
|
result.append((*i)->file_->path_);
|
|
|
|
}
|
|
|
|
} else if (var == "$out") {
|
|
|
|
result = edge_->outputs_[0]->file_->path_;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
Edge* edge_;
|
|
|
|
};
|
|
|
|
|
2010-10-14 23:45:57 -07:00
|
|
|
string Edge::EvaluateCommand() {
|
2010-10-15 00:15:42 -07:00
|
|
|
EdgeEnv env(this);
|
|
|
|
return rule_->command_.Evaluate(&env);
|
2010-10-14 23:45:57 -07:00
|
|
|
}
|
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
struct StatCache {
|
2010-10-14 23:01:22 -07:00
|
|
|
typedef map<string, FileStat*> Paths;
|
|
|
|
Paths paths_;
|
|
|
|
FileStat* GetFile(const string& path);
|
2010-10-17 19:13:07 -07:00
|
|
|
void Dump();
|
2010-10-18 11:58:03 -07:00
|
|
|
void Reload();
|
2010-10-14 22:14:06 -07:00
|
|
|
};
|
2010-10-14 23:01:22 -07:00
|
|
|
|
|
|
|
FileStat* StatCache::GetFile(const string& path) {
|
|
|
|
Paths::iterator i = paths_.find(path);
|
|
|
|
if (i != paths_.end())
|
|
|
|
return i->second;
|
|
|
|
FileStat* file = new FileStat(path);
|
|
|
|
paths_[path] = file;
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2010-10-17 19:13:07 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
void StatCache::Dump() {
|
|
|
|
for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
|
|
|
|
printf("%s %s\n", i->second->path_.c_str(),
|
|
|
|
i->second->node_->dirty_ ? "dirty" : "clean");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-18 11:58:03 -07:00
|
|
|
void StatCache::Reload() {
|
|
|
|
for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
|
|
|
|
i->second->Stat();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
struct State {
|
|
|
|
StatCache stat_cache_;
|
|
|
|
map<string, Rule*> rules_;
|
|
|
|
vector<Edge*> edges_;
|
|
|
|
|
2010-10-14 23:01:22 -07:00
|
|
|
StatCache* stat_cache() { return &stat_cache_; }
|
|
|
|
|
2010-10-14 22:14:06 -07:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
|
|
|
Rule* State::AddRule(const string& name, const string& command) {
|
|
|
|
Rule* rule = new Rule(name, command);
|
|
|
|
rules_[name] = rule;
|
|
|
|
return rule;
|
|
|
|
}
|
|
|
|
|
|
|
|
Edge* State::AddEdge(const string& rule_name) {
|
|
|
|
return AddEdge(rules_[rule_name]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Edge* State::AddEdge(Rule* rule) {
|
|
|
|
Edge* edge = new Edge();
|
|
|
|
edge->rule_ = rule;
|
|
|
|
edges_.push_back(edge);
|
|
|
|
return edge;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* State::GetNode(const string& path) {
|
2010-10-14 23:01:22 -07:00
|
|
|
FileStat* file = stat_cache_.GetFile(path);
|
|
|
|
if (!file->node_)
|
|
|
|
file->node_ = new Node(file);
|
|
|
|
return file->node_;
|
2010-10-14 22:14:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void State::AddInOut(Edge* edge, Edge::InOut inout, const string& path) {
|
|
|
|
Node* node = GetNode(path);
|
2010-10-14 23:22:53 -07:00
|
|
|
if (inout == Edge::IN) {
|
2010-10-14 22:14:06 -07:00
|
|
|
edge->inputs_.push_back(node);
|
2010-10-14 23:22:53 -07:00
|
|
|
node->out_edges_.push_back(edge);
|
|
|
|
} else {
|
2010-10-14 22:14:06 -07:00
|
|
|
edge->outputs_.push_back(node);
|
2010-10-14 23:22:53 -07:00
|
|
|
assert(node->in_edge_ == NULL);
|
|
|
|
node->in_edge_ = edge;
|
|
|
|
}
|
2010-10-14 22:14:06 -07:00
|
|
|
}
|
2010-10-14 23:40:26 -07:00
|
|
|
|
|
|
|
struct Plan {
|
2010-10-16 22:17:37 -07:00
|
|
|
explicit Plan(State* state) : state_(state) {}
|
2010-10-14 23:40:26 -07:00
|
|
|
|
|
|
|
void AddTarget(const string& path);
|
|
|
|
bool AddTarget(Node* node);
|
|
|
|
|
|
|
|
Edge* FindWork();
|
2010-10-17 19:13:07 -07:00
|
|
|
void EdgeFinished(Edge* edge);
|
|
|
|
void NodeFinished(Node* node);
|
2010-10-14 23:40:26 -07:00
|
|
|
|
|
|
|
State* state_;
|
|
|
|
set<Node*> want_;
|
|
|
|
queue<Edge*> ready_;
|
2010-10-16 22:17:37 -07:00
|
|
|
|
|
|
|
private:
|
|
|
|
Plan();
|
|
|
|
Plan(const Plan&);
|
2010-10-14 23:40:26 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
void Plan::AddTarget(const string& path) {
|
|
|
|
AddTarget(state_->GetNode(path));
|
|
|
|
}
|
|
|
|
bool Plan::AddTarget(Node* node) {
|
|
|
|
if (!node->dirty())
|
|
|
|
return false;
|
|
|
|
Edge* edge = node->in_edge_;
|
2010-10-17 18:15:14 -07:00
|
|
|
assert(edge); // Only nodes with in-edges can be dirty.
|
2010-10-14 23:40:26 -07:00
|
|
|
|
|
|
|
want_.insert(node);
|
|
|
|
|
|
|
|
bool awaiting_inputs = false;
|
2010-10-17 19:13:07 -07:00
|
|
|
for (vector<Node*>::iterator i = edge->inputs_.begin();
|
|
|
|
i != edge->inputs_.end(); ++i) {
|
2010-10-14 23:40:26 -07:00
|
|
|
if (AddTarget(*i))
|
|
|
|
awaiting_inputs = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!awaiting_inputs)
|
|
|
|
ready_.push(edge);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Edge* Plan::FindWork() {
|
|
|
|
if (ready_.empty())
|
|
|
|
return NULL;
|
|
|
|
Edge* edge = ready_.front();
|
|
|
|
ready_.pop();
|
|
|
|
return edge;
|
|
|
|
}
|
2010-10-15 18:08:10 -07:00
|
|
|
|
2010-10-17 19:13:07 -07:00
|
|
|
void Plan::EdgeFinished(Edge* edge) {
|
|
|
|
// Check off any nodes we were waiting for with this edge.
|
|
|
|
for (vector<Node*>::iterator i = edge->outputs_.begin();
|
|
|
|
i != edge->outputs_.end(); ++i) {
|
|
|
|
set<Node*>::iterator j = want_.find(*i);
|
|
|
|
if (j != want_.end()) {
|
|
|
|
NodeFinished(*j);
|
|
|
|
want_.erase(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Plan::NodeFinished(Node* node) {
|
|
|
|
// See if we we want any edges from this node.
|
|
|
|
for (vector<Edge*>::iterator i = node->out_edges_.begin();
|
|
|
|
i != node->out_edges_.end(); ++i) {
|
|
|
|
// See if we want any outputs from this edge.
|
|
|
|
for (vector<Node*>::iterator j = (*i)->outputs_.begin();
|
|
|
|
j != (*i)->outputs_.end(); ++j) {
|
|
|
|
if (want_.find(*j) != want_.end()) {
|
|
|
|
// See if the edge is ready.
|
|
|
|
// XXX just track dirty counts.
|
|
|
|
// XXX may double-enqueue edge.
|
|
|
|
bool ready = true;
|
|
|
|
for (vector<Node*>::iterator k = (*i)->inputs_.begin();
|
|
|
|
k != (*i)->inputs_.end(); ++k) {
|
|
|
|
if ((*k)->dirty()) {
|
|
|
|
ready = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ready)
|
|
|
|
ready_.push(*i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-16 11:05:58 -07:00
|
|
|
#include "manifest_parser.h"
|
2010-10-16 22:17:37 -07:00
|
|
|
|
|
|
|
struct Shell {
|
|
|
|
virtual ~Shell() {}
|
2010-10-17 23:34:15 -07:00
|
|
|
virtual bool RunCommand(Edge* edge);
|
2010-10-16 22:17:37 -07:00
|
|
|
};
|
|
|
|
|
2010-10-17 23:34:15 -07:00
|
|
|
bool Shell::RunCommand(Edge* edge) {
|
|
|
|
string err;
|
|
|
|
string command = edge->EvaluateCommand();
|
|
|
|
printf(" %s\n", command.c_str());
|
|
|
|
int ret = system(command.c_str());
|
|
|
|
if (WIFEXITED(ret)) {
|
|
|
|
int exit = WEXITSTATUS(ret);
|
|
|
|
if (exit == 0)
|
|
|
|
return true;
|
|
|
|
err = "nonzero exit status";
|
|
|
|
} else {
|
|
|
|
err = "something else went wrong";
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-10-16 22:17:37 -07:00
|
|
|
struct Builder {
|
|
|
|
Builder(State* state) : plan_(state) {}
|
|
|
|
virtual ~Builder() {}
|
|
|
|
|
|
|
|
void AddTarget(const string& name) {
|
|
|
|
plan_.AddTarget(name);
|
|
|
|
}
|
|
|
|
bool Build(Shell* shell, string* err);
|
|
|
|
|
|
|
|
Plan plan_;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool Builder::Build(Shell* shell, string* err) {
|
2010-10-17 18:15:14 -07:00
|
|
|
if (plan_.want_.empty()) {
|
|
|
|
*err = "no work to do";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Edge* edge = plan_.FindWork();
|
|
|
|
if (!edge) {
|
|
|
|
*err = "unable to find work";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
2010-10-16 22:17:37 -07:00
|
|
|
string command = edge->EvaluateCommand();
|
2010-10-17 10:06:51 -07:00
|
|
|
if (!shell->RunCommand(edge)) {
|
2010-10-16 22:17:37 -07:00
|
|
|
err->assign("command '" + command + "' failed.");
|
|
|
|
return false;
|
|
|
|
}
|
2010-10-17 19:13:07 -07:00
|
|
|
for (vector<Node*>::iterator i = edge->outputs_.begin();
|
|
|
|
i != edge->outputs_.end(); ++i) {
|
|
|
|
// XXX check that the output actually changed
|
|
|
|
// XXX just notify node and have it propagate?
|
|
|
|
(*i)->dirty_ = false;
|
|
|
|
}
|
|
|
|
plan_.EdgeFinished(edge);
|
2010-10-17 18:15:14 -07:00
|
|
|
} while ((edge = plan_.FindWork()) != NULL);
|
2010-10-17 19:13:07 -07:00
|
|
|
|
|
|
|
if (!plan_.want_.empty()) {
|
|
|
|
*err = "ran out of work";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-10-16 22:17:37 -07:00
|
|
|
return true;
|
|
|
|
}
|