merge FileStat into Node
authorEvan Martin <martine@danga.com>
Wed, 7 Dec 2011 16:45:16 +0000 (08:45 -0800)
committerEvan Martin <martine@danga.com>
Wed, 7 Dec 2011 16:47:38 +0000 (08:47 -0800)
The two were always one-to-one anyway.  I started adding accessors
to FileStat and then realized most users wanted them on Node and
that forwarding them through was silly.

14 files changed:
src/build.cc
src/build_log.cc
src/build_test.cc
src/clean.cc
src/disk_interface_test.cc
src/graph.cc
src/graph.h
src/graph_test.cc
src/graphviz.cc
src/ninja.cc
src/parsers_test.cc
src/stat_cache.cc
src/stat_cache.h
src/state.cc

index 3da915d..6da821f 100644 (file)
@@ -195,8 +195,8 @@ bool Plan::AddSubTarget(Node* node, vector<Node*>* stack, string* err) {
     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;
@@ -256,7 +256,7 @@ bool Plan::CheckDependencyCycle(Node* node, vector<Node*>* stack, string* err) {
   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;
 }
@@ -324,8 +324,8 @@ void Plan::CleanNode(BuildLog* build_log, Node* node) {
       // 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.
@@ -454,7 +454,7 @@ Node* Builder::AddTarget(const string& name, string* err) {
 }
 
 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;
@@ -555,7 +555,7 @@ bool Builder::StartEdge(Edge* edge, string* err) {
   // 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;
   }
 
@@ -578,9 +578,9 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
 
       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);
@@ -594,7 +594,7 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
         // (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;
index 7fd9ca1..852a9d0 100644 (file)
@@ -73,7 +73,7 @@ void BuildLog::RecordCommand(Edge* edge, int start_time, int end_time,
   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()) {
index b1879ff..eb9ead0 100644 (file)
@@ -38,8 +38,8 @@ TEST_F(PlanTest, Basic) {
 
   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());
 
@@ -47,8 +47,8 @@ TEST_F(PlanTest, Basic) {
 
   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);
 
@@ -222,7 +222,7 @@ void BuildTest::Dirty(const string& path) {
   // 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() {
@@ -237,7 +237,7 @@ bool BuildTest::StartCommand(Edge* edge) {
       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") {
@@ -508,10 +508,10 @@ TEST_F(BuildTest, OrderOnlyDeps) {
   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());
index 8ae4385..c768629 100644 (file)
@@ -109,7 +109,7 @@ int Cleaner::CleanAll(bool generator) {
       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());
@@ -120,7 +120,7 @@ int Cleaner::CleanAll(bool generator) {
 
 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) {
@@ -182,7 +182,7 @@ void Cleaner::DoCleanRule(const Rule* rule) {
       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) {
index 107726b..a7f94bb 100644 (file)
@@ -193,7 +193,7 @@ TEST_F(StatTest, Simple) {
 "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);
@@ -208,7 +208,7 @@ TEST_F(StatTest, TwoStep) {
 "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);
@@ -227,7 +227,7 @@ TEST_F(StatTest, Tree) {
 "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);
@@ -247,7 +247,7 @@ TEST_F(StatTest, Middle) {
   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);
index 818eb4f..0aa155b 100644 (file)
@@ -23,7 +23,7 @@
 #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;
 }
@@ -41,13 +41,13 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
   // 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();
       }
     }
 
@@ -63,8 +63,8 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
       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();
       }
     }
   }
@@ -77,7 +77,7 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
 
     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;
@@ -88,7 +88,7 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
   // 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;
   }
@@ -107,23 +107,23 @@ bool Edge::RecomputeOutputDirty(BuildLog* build_log, time_t most_recent_input,
   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 {
@@ -135,7 +135,7 @@ bool Edge::RecomputeOutputDirty(BuildLog* build_log, time_t most_recent_input,
   // 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;
   }
@@ -164,14 +164,14 @@ struct EdgeEnv : public Env {
            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);
@@ -213,10 +213,10 @@ bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface,
   }
 
   // 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;
   }
 
@@ -260,11 +260,11 @@ bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface,
 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");
 }
index 810d4ec..a52a3d7 100644 (file)
@@ -23,11 +23,13 @@ using namespace std;
 
 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);
@@ -40,6 +42,17 @@ struct FileStat {
     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;
   }
@@ -48,13 +61,25 @@ struct FileStat {
     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.).
@@ -134,17 +159,4 @@ struct Edge {
   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_
index e221089..8ab921e 100644 (file)
@@ -127,7 +127,7 @@ TEST_F(GraphTest, RootNodes) {
   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));
   }
 }
index aa75ea1..aa5bb08 100644 (file)
@@ -22,7 +22,7 @@ void GraphViz::AddTarget(Node* node) {
   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_) {
index 86c9a59..f0525fb 100644 (file)
@@ -168,7 +168,7 @@ bool CollectTargetsFromArgs(State* state, int argc, char* argv[],
 
         Node* suggestion = state->SpellcheckNode(path);
         if (suggestion) {
-          *err += ", did you mean '" + suggestion->file_->path_ + "'?";
+          *err += ", did you mean '" + suggestion->path() + "'?";
         }
         return false;
       }
@@ -207,7 +207,7 @@ int CmdQuery(State* state, int argc, char* argv[]) {
         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();
@@ -215,14 +215,14 @@ int CmdQuery(State* state, int argc, char* argv[]) {
         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]);
       }
@@ -257,7 +257,7 @@ int CmdTargetsList(const vector<Node*>& nodes, int depth, int indent) {
        ++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)
@@ -281,7 +281,7 @@ int CmdTargetsSourceList(State* state) {
          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;
 }
 
@@ -294,7 +294,7 @@ int CmdTargetsList(State* state, const string& rule_name) {
     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());
       }
     }
   }
@@ -314,7 +314,7 @@ int CmdTargetsList(State* state) {
     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());
     }
   }
index a99b510..4ef4edb 100644 (file)
@@ -482,9 +482,9 @@ TEST_F(ParserTest, DefaultStatements) {
   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) {
index 368f545..e414d41 100644 (file)
 #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;
     }
@@ -47,17 +54,15 @@ FileStat* StatCache::SpellcheckFile(const std::string& path) {
 
 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();
 }
index 2a5b38b..b7d6e8a 100644 (file)
 #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_;
 };
 
index 9519856..bbb2f6a 100644 (file)
@@ -46,24 +46,15 @@ Edge* State::AddEdge(const Rule* rule) {
 }
 
 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) {