if (node->dirty_) {
string referenced;
if (!stack->empty())
- referenced = ", needed by '" + stack->back()->file_->path_ + "',";
- *err = "'" + node->file_->path_ + "'" + referenced + " missing "
+ referenced = ", needed by '" + stack->back()->path() + "',";
+ *err = "'" + node->path() + "'" + referenced + " missing "
"and no known rule to make it";
}
return false;
for (vector<Node*>::iterator i = start; i != stack->end(); ++i) {
if (i != start)
err->append(" -> ");
- err->append((*i)->file_->path_);
+ err->append((*i)->path());
}
return true;
}
// Recompute most_recent_input and command.
time_t most_recent_input = 1;
for (vector<Node*>::iterator ni = begin; ni != end; ++ni)
- if ((*ni)->file_->mtime_ > most_recent_input)
- most_recent_input = (*ni)->file_->mtime_;
+ if ((*ni)->mtime() > most_recent_input)
+ most_recent_input = (*ni)->mtime();
string command = (*ei)->EvaluateCommand();
// Now, recompute the dirty state of each output.
}
bool Builder::AddTarget(Node* node, string* err) {
- node->file_->StatIfNecessary(disk_interface_);
+ node->StatIfNecessary(disk_interface_);
if (Edge* in_edge = node->in_edge_) {
if (!in_edge->RecomputeDirty(state_, disk_interface_, err))
return false;
// XXX: this will block; do we care?
for (vector<Node*>::iterator i = edge->outputs_.begin();
i != edge->outputs_.end(); ++i) {
- if (!disk_interface_->MakeDirs((*i)->file_->path_))
+ if (!disk_interface_->MakeDirs((*i)->path()))
return false;
}
for (vector<Node*>::iterator i = edge->outputs_.begin();
i != edge->outputs_.end(); ++i) {
- if ((*i)->file_->exists()) {
- time_t new_mtime = disk_interface_->Stat((*i)->file_->path_);
- if ((*i)->file_->mtime_ == new_mtime) {
+ if ((*i)->exists()) {
+ time_t new_mtime = disk_interface_->Stat((*i)->path());
+ if ((*i)->mtime() == new_mtime) {
// The rule command did not change the output. Propagate the clean
// state through the build graph.
plan_.CleanNode(log_, *i);
// (existing) non-order-only input or the depfile.
for (vector<Node*>::iterator i = edge->inputs_.begin();
i != edge->inputs_.end() - edge->order_only_deps_; ++i) {
- time_t input_mtime = disk_interface_->Stat((*i)->file_->path_);
+ time_t input_mtime = disk_interface_->Stat((*i)->path());
if (input_mtime == 0) {
restat_mtime = 0;
break;
const string command = edge->EvaluateCommand();
for (vector<Node*>::iterator out = edge->outputs_.begin();
out != edge->outputs_.end(); ++out) {
- const string& path = (*out)->file_->path_;
+ const string& path = (*out)->path();
Log::iterator i = log_.find(path.c_str());
LogEntry* log_entry;
if (i != log_.end()) {
Edge* edge = plan_.FindWork();
ASSERT_TRUE(edge);
- ASSERT_EQ("in", edge->inputs_[0]->file_->path_);
- ASSERT_EQ("mid", edge->outputs_[0]->file_->path_);
+ ASSERT_EQ("in", edge->inputs_[0]->path());
+ ASSERT_EQ("mid", edge->outputs_[0]->path());
ASSERT_FALSE(plan_.FindWork());
edge = plan_.FindWork();
ASSERT_TRUE(edge);
- ASSERT_EQ("mid", edge->inputs_[0]->file_->path_);
- ASSERT_EQ("out", edge->outputs_[0]->file_->path_);
+ ASSERT_EQ("mid", edge->inputs_[0]->path());
+ ASSERT_EQ("out", edge->outputs_[0]->path());
plan_.EdgeFinished(edge);
// If it's an input file, mark that we've already stat()ed it and
// it's missing.
if (!node->in_edge_)
- node->file_->mtime_ = 0;
+ node->MarkMissing();
}
bool BuildTest::CanRunMore() {
edge->rule().name_ == "touch") {
for (vector<Node*>::iterator out = edge->outputs_.begin();
out != edge->outputs_.end(); ++out) {
- fs_.Create((*out)->file_->path_, now_, "");
+ fs_.Create((*out)->path(), now_, "");
}
} else if (edge->rule().name_ == "true" ||
edge->rule().name_ == "fail") {
EXPECT_EQ(1, edge->order_only_deps_);
// Verify the inputs are in the order we expect
// (explicit then implicit then orderonly).
- EXPECT_EQ("foo.c", edge->inputs_[0]->file_->path_);
- EXPECT_EQ("blah.h", edge->inputs_[1]->file_->path_);
- EXPECT_EQ("bar.h", edge->inputs_[2]->file_->path_);
- EXPECT_EQ("otherfile", edge->inputs_[3]->file_->path_);
+ EXPECT_EQ("foo.c", edge->inputs_[0]->path());
+ EXPECT_EQ("blah.h", edge->inputs_[1]->path());
+ EXPECT_EQ("bar.h", edge->inputs_[2]->path());
+ EXPECT_EQ("otherfile", edge->inputs_[3]->path());
// Expect the command line we generate to only use the original input.
ASSERT_EQ("cc foo.c", edge->EvaluateCommand());
continue;
for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
out_node != (*e)->outputs_.end(); ++out_node) {
- Remove((*out_node)->file_->path_);
+ Remove((*out_node)->path());
}
if (!(*e)->rule().depfile_.empty())
Remove((*e)->EvaluateDepFile());
void Cleaner::DoCleanTarget(Node* target) {
if (target->in_edge_) {
- Remove(target->file_->path_);
+ Remove(target->path());
for (vector<Node*>::iterator n = target->in_edge_->inputs_.begin();
n != target->in_edge_->inputs_.end();
++n) {
for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
out_node != (*e)->outputs_.end();
++out_node)
- Remove((*out_node)->file_->path_);
+ Remove((*out_node)->path());
}
int Cleaner::CleanRule(const Rule* rule) {
"build out: cat in\n"));
Node* out = GetNode("out");
- out->file_->Stat(this);
+ out->Stat(this);
ASSERT_EQ(1u, stats_.size());
Edge* edge = out->in_edge_;
edge->RecomputeDirty(NULL, this, NULL);
"build mid: cat in\n"));
Node* out = GetNode("out");
- out->file_->Stat(this);
+ out->Stat(this);
ASSERT_EQ(1u, stats_.size());
Edge* edge = out->in_edge_;
edge->RecomputeDirty(NULL, this, NULL);
"build mid2: cat in21 in22\n"));
Node* out = GetNode("out");
- out->file_->Stat(this);
+ out->Stat(this);
ASSERT_EQ(1u, stats_.size());
Edge* edge = out->in_edge_;
edge->RecomputeDirty(NULL, this, NULL);
mtimes_["out"] = 1;
Node* out = GetNode("out");
- out->file_->Stat(this);
+ out->Stat(this);
ASSERT_EQ(1u, stats_.size());
Edge* edge = out->in_edge_;
edge->RecomputeDirty(NULL, this, NULL);
#include "state.h"
#include "util.h"
-bool FileStat::Stat(DiskInterface* disk_interface) {
+bool Node::Stat(DiskInterface* disk_interface) {
mtime_ = disk_interface->Stat(path_);
return mtime_ > 0;
}
// Visit all inputs; we're dirty if any of the inputs are dirty.
time_t most_recent_input = 1;
for (vector<Node*>::iterator i = inputs_.begin(); i != inputs_.end(); ++i) {
- if ((*i)->file_->StatIfNecessary(disk_interface)) {
+ if ((*i)->StatIfNecessary(disk_interface)) {
if (Edge* edge = (*i)->in_edge_) {
if (!edge->RecomputeDirty(state, disk_interface, err))
return false;
} else {
// This input has no in-edge; it is dirty if it is missing.
- (*i)->dirty_ = !(*i)->file_->exists();
+ (*i)->dirty_ = !(*i)->exists();
}
}
if ((*i)->dirty_) {
dirty = true;
} else {
- if ((*i)->file_->mtime_ > most_recent_input)
- most_recent_input = (*i)->file_->mtime_;
+ if ((*i)->mtime() > most_recent_input)
+ most_recent_input = (*i)->mtime();
}
}
}
for (vector<Node*>::iterator i = outputs_.begin();
i != outputs_.end(); ++i) {
- (*i)->file_->StatIfNecessary(disk_interface);
+ (*i)->StatIfNecessary(disk_interface);
if (RecomputeOutputDirty(build_log, most_recent_input, command, *i)) {
dirty = true;
break;
// Finally, visit each output to mark off that we've visited it, and update
// their dirty state if necessary.
for (vector<Node*>::iterator i = outputs_.begin(); i != outputs_.end(); ++i) {
- (*i)->file_->StatIfNecessary(disk_interface);
+ (*i)->StatIfNecessary(disk_interface);
if (dirty)
(*i)->dirty_ = true;
}
if (is_phony()) {
// Phony edges don't write any output.
// Outputs are only dirty if there are no inputs and we're missing the output.
- return inputs_.empty() && !output->file_->exists();
+ return inputs_.empty() && !output->exists();
}
BuildLog::LogEntry* entry = 0;
// Dirty if we're missing the output.
- if (!output->file_->exists())
+ if (!output->exists())
return true;
// Dirty if the output is older than the input.
- if (output->file_->mtime_ < most_recent_input) {
+ if (output->mtime() < most_recent_input) {
// If this is a restat rule, we may have cleaned the output with a restat
// rule in a previous run and stored the most recent input mtime in the
// build log. Use that mtime instead, so that the file will only be
// considered dirty if an input was modified since the previous run.
if (rule_->restat_ && build_log &&
- (entry = build_log->LookupByOutput(output->file_->path_))) {
+ (entry = build_log->LookupByOutput(output->path()))) {
if (entry->restat_mtime < most_recent_input)
return true;
} else {
// But if this is a generator rule, the command changing does not make us
// dirty.
if (!rule_->generator_ && build_log &&
- (entry || (entry = build_log->LookupByOutput(output->file_->path_)))) {
+ (entry || (entry = build_log->LookupByOutput(output->path())))) {
if (command != entry->command)
return true;
}
i != edge_->inputs_.end() && explicit_deps; ++i, --explicit_deps) {
if (!result.empty())
result.push_back(' ');
- result.append((*i)->file_->path_);
+ result.append((*i)->path());
}
} else if (var == "out") {
for (vector<Node*>::iterator i = edge_->outputs_.begin();
i != edge_->outputs_.end(); ++i) {
if (!result.empty())
result.push_back(' ');
- result.append((*i)->file_->path_);
+ result.append((*i)->path());
}
} else if (edge_->env_) {
return edge_->env_->LookupVariable(var);
}
// Check that this depfile matches our output.
- StringPiece opath = StringPiece(outputs_[0]->file_->path_);
+ StringPiece opath = StringPiece(outputs_[0]->path());
if (opath != makefile.out_) {
*err = "expected depfile '" + path + "' to mention '" +
- outputs_[0]->file_->path_ + "', got '" + makefile.out_.AsString() + "'";
+ outputs_[0]->path() + "', got '" + makefile.out_.AsString() + "'";
return false;
}
void Edge::Dump() {
printf("[ ");
for (vector<Node*>::iterator i = inputs_.begin(); i != inputs_.end(); ++i) {
- printf("%s ", (*i)->file_->path_.c_str());
+ printf("%s ", (*i)->path().c_str());
}
printf("--%s-> ", rule_->name_.c_str());
for (vector<Node*>::iterator i = outputs_.begin(); i != outputs_.end(); ++i) {
- printf("%s ", (*i)->file_->path_.c_str());
+ printf("%s ", (*i)->path().c_str());
}
printf("]\n");
}
struct DiskInterface;
-struct Node;
+struct Edge;
-/// Information about a single on-disk file: path, mtime.
-struct FileStat {
- FileStat(const string& path) : path_(path), mtime_(-1), node_(NULL) {}
+/// Information about a node in the dependency graph: the file, whether
+/// it's dirty, mtime, etc.
+struct Node {
+ Node(const string& path) : path_(path), mtime_(-1), dirty_(false),
+ in_edge_(NULL) {}
/// Return true if the file exists (mtime_ got a value).
bool Stat(DiskInterface* disk_interface);
return true;
}
+ /// Mark as not-yet-stat()ed and not dirty.
+ void ResetState() {
+ mtime_ = -1;
+ dirty_ = false;
+ }
+
+ /// Mark the Node as already-stat()ed and missing.
+ void MarkMissing() {
+ mtime_ = 0;
+ }
+
bool exists() const {
return mtime_ != 0;
}
return mtime_ != -1;
}
+ const string& path() const { return path_; }
+ time_t mtime() const { return mtime_; }
+
+ bool dirty() const { return dirty_; }
+
+private:
string path_;
// Possible values of mtime_:
// -1: file hasn't been examined
// 0: we looked, and file doesn't exist
// >0: actual file's mtime
time_t mtime_;
- Node* node_;
+
+ // TODO: make these private as well. But don't just blindly add
+ // setters/getters, instead pay attention to the proper API.
+public:
+ bool dirty_;
+ Edge* in_edge_;
+ vector<Edge*> out_edges_;
};
/// An invokable build command and associated metadata (description, etc.).
bool is_phony() const;
};
-/// Information about a node in the dependency graph: the file, whether
-/// it's dirty, etc.
-struct Node {
- Node(FileStat* file) : file_(file), dirty_(false), in_edge_(NULL) {}
-
- bool dirty() const { return dirty_; }
-
- FileStat* file_;
- bool dirty_;
- Edge* in_edge_;
- vector<Edge*> out_edges_;
-};
-
#endif // NINJA_GRAPH_H_
vector<Node*> root_nodes = state_.RootNodes(&err);
EXPECT_EQ(4u, root_nodes.size());
for (size_t i = 0; i < root_nodes.size(); ++i) {
- string name = root_nodes[i]->file_->path_;
+ string name = root_nodes[i]->path();
EXPECT_EQ("out", name.substr(0, 3));
}
}
if (visited_.find(node) != visited_.end())
return;
- printf("\"%p\" [label=\"%s\"]\n", node, node->file_->path_.c_str());
+ printf("\"%p\" [label=\"%s\"]\n", node, node->path().c_str());
visited_.insert(node);
if (!node->in_edge_) {
Node* suggestion = state->SpellcheckNode(path);
if (suggestion) {
- *err += ", did you mean '" + suggestion->file_->path_ + "'?";
+ *err += ", did you mean '" + suggestion->path() + "'?";
}
return false;
}
printf(" input: %s\n", node->in_edge_->rule_->name_.c_str());
for (vector<Node*>::iterator in = node->in_edge_->inputs_.begin();
in != node->in_edge_->inputs_.end(); ++in) {
- printf(" %s\n", (*in)->file_->path_.c_str());
+ printf(" %s\n", (*in)->path().c_str());
}
}
for (vector<Edge*>::iterator edge = node->out_edges_.begin();
printf(" output: %s\n", (*edge)->rule_->name_.c_str());
for (vector<Node*>::iterator out = (*edge)->outputs_.begin();
out != (*edge)->outputs_.end(); ++out) {
- printf(" %s\n", (*out)->file_->path_.c_str());
+ printf(" %s\n", (*out)->path().c_str());
}
}
} else {
Node* suggestion = state->SpellcheckNode(argv[i]);
if (suggestion) {
printf("%s unknown, did you mean %s?\n",
- argv[i], suggestion->file_->path_.c_str());
+ argv[i], suggestion->path().c_str());
} else {
printf("%s unknown\n", argv[i]);
}
++n) {
for (int i = 0; i < indent; ++i)
printf(" ");
- const char* target = (*n)->file_->path_.c_str();
+ const char* target = (*n)->path().c_str();
if ((*n)->in_edge_) {
printf("%s: %s\n", target, (*n)->in_edge_->rule_->name_.c_str());
if (depth > 1 || depth <= 0)
inps != (*e)->inputs_.end();
++inps)
if (!(*inps)->in_edge_)
- printf("%s\n", (*inps)->file_->path_.c_str());
+ printf("%s\n", (*inps)->path().c_str());
return 0;
}
if ((*e)->rule_->name_ == rule_name) {
for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
out_node != (*e)->outputs_.end(); ++out_node) {
- rules.insert((*out_node)->file_->path_);
+ rules.insert((*out_node)->path());
}
}
}
for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();
out_node != (*e)->outputs_.end(); ++out_node) {
printf("%s: %s\n",
- (*out_node)->file_->path_.c_str(),
+ (*out_node)->path().c_str(),
(*e)->rule_->name_.c_str());
}
}
std::vector<Node*> nodes = state.DefaultNodes(&err);
EXPECT_EQ("", err);
ASSERT_EQ(3u, nodes.size());
- EXPECT_EQ("a", nodes[0]->file_->path_);
- EXPECT_EQ("b", nodes[1]->file_->path_);
- EXPECT_EQ("c", nodes[2]->file_->path_);
+ EXPECT_EQ("a", nodes[0]->path());
+ EXPECT_EQ("b", nodes[1]->path());
+ EXPECT_EQ("c", nodes[2]->path());
}
TEST(MakefileParser, Basic) {
#include "edit_distance.h"
#include "graph.h"
-FileStat* StatCache::GetFile(const std::string& path) {
+Node* StatCache::GetFile(const std::string& path) {
+ Node* node = LookupFile(path);
+ if (node)
+ return node;
+ node = new Node(path);
+ paths_[node->path().c_str()] = node;
+ return node;
+}
+
+Node* StatCache::LookupFile(const std::string& path) {
Paths::iterator i = paths_.find(path.c_str());
if (i != paths_.end())
return i->second;
- FileStat* file = new FileStat(path);
- paths_[file->path_.c_str()] = file;
- return file;
+ return NULL;
}
-FileStat* StatCache::SpellcheckFile(const std::string& path) {
+Node* StatCache::SpellcheckFile(const std::string& path) {
const bool kAllowReplacements = true;
const int kMaxValidEditDistance = 3;
int min_distance = kMaxValidEditDistance + 1;
- FileStat* result = NULL;
+ Node* result = NULL;
for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
int distance = EditDistance(
i->first, path, kAllowReplacements, kMaxValidEditDistance);
- if (distance < min_distance && i->second->node_) {
+ if (distance < min_distance && i->second) {
min_distance = distance;
result = i->second;
}
void StatCache::Dump() {
for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
- FileStat* file = i->second;
+ Node* node = i->second;
printf("%s %s\n",
- file->path_.c_str(),
- file->status_known() ? (file->node_->dirty_ ? "dirty" : "clean")
+ node->path().c_str(),
+ node->status_known() ? (node->dirty() ? "dirty" : "clean")
: "unknown");
}
}
void StatCache::Invalidate() {
- for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
- i->second->mtime_ = -1;
- i->second->node_->dirty_ = false;
- }
+ for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i)
+ i->second->ResetState();
}
#define NINJA_STAT_CACHE_H_
#include <string>
+using namespace std;
#include "hash_map.h"
#include <string.h>
-struct FileStat;
+struct Node;
-/// Mapping of path -> FileStat.
+/// Mapping of path -> Node.
struct StatCache {
- FileStat* GetFile(const std::string& path);
- FileStat* SpellcheckFile(const std::string& path);
+ Node* GetFile(const string& path);
+ Node* LookupFile(const string& path);
+ Node* SpellcheckFile(const string& path);
/// Dump the mapping to stdout (useful for debugging).
void Dump();
void Invalidate();
- typedef ExternalStringHashMap<FileStat*>::Type Paths;
+ typedef ExternalStringHashMap<Node*>::Type Paths;
Paths paths_;
};
}
Node* State::GetNode(const string& path) {
- FileStat* file = stat_cache_.GetFile(path);
- if (!file->node_)
- file->node_ = new Node(file);
- return file->node_;
+ return stat_cache_.GetFile(path);
}
Node* State::LookupNode(const string& path) {
- FileStat* file = stat_cache_.GetFile(path);
- if (!file->node_)
- return NULL;
- return file->node_;
+ return stat_cache_.LookupFile(path);
}
Node* State::SpellcheckNode(const string& path) {
- FileStat* file = stat_cache_.SpellcheckFile(path);
- if (!file || !file->node_)
- return NULL;
- return file->node_;
+ return stat_cache_.SpellcheckFile(path);
}
void State::AddIn(Edge* edge, const string& path) {