ninja/ninja.h

270 lines
5.9 KiB
C
Raw Normal View History

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-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();
}
void Node::MarkDirty() {
if (dirty_)
return; // We already know.
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-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-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 {
Plan(State* state) : state_(state) {}
void AddTarget(const string& path);
bool AddTarget(Node* node);
Edge* FindWork();
State* state_;
set<Node*> want_;
queue<Edge*> ready_;
};
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_;
if (!edge) {
// TODO: if file doesn't exist we should die here.
return false;
}
want_.insert(node);
bool awaiting_inputs = false;
for (vector<Node*>::iterator i = edge->inputs_.begin(); i != edge->inputs_.end(); ++i) {
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-16 11:05:58 -07:00
#include "manifest_parser.h"