ninja/ninja.h

184 lines
3.8 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
};
struct Rule {
Rule(const string& name, const string& command) :
name_(name), command_(command) {}
string name_;
string command_;
};
struct Edge {
2010-10-14 23:01:22 -07:00
Edge() : rule_(NULL) {}
void MarkDirty(Node* node);
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-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;
}