From 0a186ab0cbcd2015d2cf9afd8460c7b60e4bcbdd Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 8 Jan 2011 09:44:15 -0800 Subject: split out graph into its own header --- build.ninja | 5 +- src/build.cc | 1 + src/build_log.cc | 1 + src/graph.cc | 201 +++++++++++++++++++++++++++++++++++++++++++++++ src/graph.h | 109 ++++++++++++++++++++++++++ src/graphviz.h | 2 + src/ninja.cc | 3 +- src/ninja.h | 109 ++------------------------ src/ninja_jumble.cc | 222 ++++------------------------------------------------ src/ninja_test.cc | 6 +- src/parsers.cc | 5 +- src/parsers_test.cc | 1 + src/test.h | 5 +- 13 files changed, 352 insertions(+), 318 deletions(-) create mode 100644 src/graph.cc create mode 100644 src/graph.h diff --git a/build.ninja b/build.ninja index c650cd2..9f5173a 100644 --- a/build.ninja +++ b/build.ninja @@ -30,13 +30,14 @@ rule link # and build that "ninja.o" by compiling "ninja.cc". build $builddir/build.o: cxx src/build.cc build $builddir/build_log.o: cxx src/build_log.cc +build $builddir/graph.o: cxx src/graph.cc build $builddir/parsers.o: cxx src/parsers.cc build $builddir/subprocess.o: cxx src/subprocess.cc build $builddir/util.o: cxx src/util.cc build $builddir/ninja_jumble.o: cxx src/ninja_jumble.cc build $builddir/ninja.a: ar $builddir/build.o $builddir/build_log.o \ - $builddir/parsers.o $builddir/subprocess.o $builddir/util.o \ - $builddir/ninja_jumble.o + $builddir/graph.o $builddir/parsers.o $builddir/subprocess.o \ + $builddir/util.o $builddir/ninja_jumble.o build $builddir/ninja.o: cxx src/ninja.cc build ninja: link $builddir/ninja.o $builddir/ninja.a diff --git a/src/build.cc b/src/build.cc index 8ffbc7c..0aa1d10 100644 --- a/src/build.cc +++ b/src/build.cc @@ -3,6 +3,7 @@ #include #include "build_log.h" +#include "graph.h" #include "ninja.h" #include "subprocess.h" diff --git a/src/build_log.cc b/src/build_log.cc index 666b80b..53fca4e 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -4,6 +4,7 @@ #include #include +#include "graph.h" #include "ninja.h" // Implementation details: diff --git a/src/graph.cc b/src/graph.cc new file mode 100644 index 0000000..22283a5 --- /dev/null +++ b/src/graph.cc @@ -0,0 +1,201 @@ +#include "graph.h" + +#include + +#include "build_log.h" +#include "ninja.h" +#include "parsers.h" + +void FileStat::Touch(int mtime) { + mtime_ = mtime; + if (node_) + node_->MarkDirty(); +} + +bool FileStat::Stat(DiskInterface* disk_interface) { + mtime_ = disk_interface->Stat(path_); + return mtime_ > 0; +} + +void Node::MarkDirty() { + if (dirty_) + return; // We already know. + + dirty_ = true; + MarkDependentsDirty(); +} + +void Node::MarkDependentsDirty() { + for (vector::iterator i = out_edges_.begin(); i != out_edges_.end(); ++i) + (*i)->MarkDirty(this); +} + +bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface, + string* err) { + bool dirty = false; + + if (!rule_->depfile_.empty()) { + if (!LoadDepFile(state, disk_interface, err)) + return false; + } + + time_t most_recent_input = 1; + for (vector::iterator i = inputs_.begin(); i != inputs_.end(); ++i) { + if ((*i)->file_->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. + // But it's ok for implicit deps to be missing. + if (!is_implicit(i - inputs_.begin())) + (*i)->dirty_ = !(*i)->file_->exists(); + } + } + + if (is_order_only(i - inputs_.begin())) { + // Order-only deps only make us dirty if they're missing. + if (!(*i)->file_->exists()) + dirty = true; + continue; + } + + // If a regular input is dirty (or missing), we're dirty. + // Otherwise consider mtime. + if ((*i)->dirty_) { + dirty = true; + } else { + if ((*i)->file_->mtime_ > most_recent_input) + most_recent_input = (*i)->file_->mtime_; + } + } + + string command = EvaluateCommand(); + + assert(!outputs_.empty()); + for (vector::iterator i = outputs_.begin(); i != outputs_.end(); ++i) { + // We may have other outputs, that our input-recursive traversal hasn't hit + // yet (or never will). Stat them if we haven't already. + (*i)->file_->StatIfNecessary(disk_interface); + + // Output is dirty if we're dirty, we're missing the output, + // or if it's older than the most recent input mtime. + if (dirty || !(*i)->file_->exists() || + (*i)->file_->mtime_ < most_recent_input) { + (*i)->dirty_ = true; + } else { + // May also be dirty due to the command changing since the last build. + BuildLog::LogEntry* entry; + if (state->build_log_ && + (entry = state->build_log_->LookupByOutput((*i)->file_->path_))) { + if (command != entry->command) + (*i)->dirty_ = true; + } + } + } + return true; +} + +void Edge::MarkDirty(Node* node) { + if (rule_ == &State::kPhonyRule) + return; + + vector::iterator i = find(inputs_.begin(), inputs_.end(), node); + if (i == inputs_.end()) + return; + if (i - inputs_.begin() >= ((int)inputs_.size()) - order_only_deps_) + return; // Order-only deps don't cause us to become dirty. + for (i = outputs_.begin(); i != outputs_.end(); ++i) + (*i)->MarkDirty(); +} + +struct EdgeEnv : public Env { + EdgeEnv(Edge* edge) : edge_(edge) {} + virtual string LookupVariable(const string& var) { + string result; + if (var == "in") { + int explicit_deps = edge_->inputs_.size() - edge_->implicit_deps_ - + edge_->order_only_deps_; + for (vector::iterator i = edge_->inputs_.begin(); + i != edge_->inputs_.end() && explicit_deps; ++i, --explicit_deps) { + if (!result.empty()) + result.push_back(' '); + result.append((*i)->file_->path_); + } + } else if (var == "out") { + result = edge_->outputs_[0]->file_->path_; + } else if (edge_->env_) { + return edge_->env_->LookupVariable(var); + } + return result; + } + Edge* edge_; +}; + +string Edge::EvaluateCommand() { + EdgeEnv env(this); + return rule_->command_.Evaluate(&env); +} + +string Edge::GetDescription() { + EdgeEnv env(this); + return rule_->description_.Evaluate(&env); +} + +bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface, string* err) { + EdgeEnv env(this); + string path = rule_->depfile_.Evaluate(&env); + + string content = disk_interface->ReadFile(path, err); + if (!err->empty()) + return false; + if (content.empty()) + return true; + + MakefileParser makefile; + if (!makefile.Parse(content, err)) + return false; + + // Check that this depfile matches our output. + if (outputs_.size() != 1) { + *err = "expected only one output"; + return false; + } + if (outputs_[0]->file_->path_ != makefile.out_) { + *err = "expected makefile to mention '" + outputs_[0]->file_->path_ + "', " + "got '" + makefile.out_ + "'"; + return false; + } + + // Add all its in-edges. + for (vector::iterator i = makefile.ins_.begin(); + i != makefile.ins_.end(); ++i) { + Node* node = state->GetNode(*i); + for (vector::iterator j = inputs_.begin(); j != inputs_.end(); ++j) { + if (*j == node) { + node = NULL; + break; + } + } + if (node) { + inputs_.insert(inputs_.end() - order_only_deps_, node); + node->out_edges_.push_back(this); + ++implicit_deps_; + } + } + + return true; +} + +void Edge::Dump() { + printf("[ "); + for (vector::iterator i = inputs_.begin(); i != inputs_.end(); ++i) { + printf("%s ", (*i)->file_->path_.c_str()); + } + printf("--%s-> ", rule_->name_.c_str()); + for (vector::iterator i = outputs_.begin(); i != outputs_.end(); ++i) { + printf("%s ", (*i)->file_->path_.c_str()); + } + printf("]\n"); +} + diff --git a/src/graph.h b/src/graph.h new file mode 100644 index 0000000..3f46db4 --- /dev/null +++ b/src/graph.h @@ -0,0 +1,109 @@ +#ifndef NINJA_GRAPH_H_ +#define NINJA_GRAPH_H_ + +#include +#include +using namespace std; + +#include "eval_env.h" + +struct DiskInterface; + +struct Node; +struct FileStat { + FileStat(const string& path) : path_(path), mtime_(-1), node_(NULL) {} + void Touch(int mtime); + // Return true if the file exists (mtime_ got a value). + bool Stat(DiskInterface* disk_interface); + + // Return true if we needed to stat. + bool StatIfNecessary(DiskInterface* disk_interface) { + if (status_known()) + return false; + Stat(disk_interface); + return true; + } + + bool exists() const { + return mtime_ != 0; + } + + bool status_known() const { + return mtime_ != -1; + } + + 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_; +}; + +struct Edge; +struct Node { + Node(FileStat* file) : file_(file), dirty_(false), in_edge_(NULL) {} + + bool dirty() const { return dirty_; } + void MarkDirty(); + void MarkDependentsDirty(); + + FileStat* file_; + bool dirty_; + Edge* in_edge_; + vector out_edges_; +}; + +struct Rule { + Rule(const string& name) : name_(name) { } + + bool ParseCommand(const string& command, string* err) { + return command_.Parse(command, err); + } + string name_; + EvalString command_; + EvalString description_; + EvalString depfile_; +}; + +struct State; +struct Edge { + Edge() : rule_(NULL), env_(NULL), implicit_deps_(0), order_only_deps_(0) {} + + void MarkDirty(Node* node); + bool RecomputeDirty(State* state, DiskInterface* disk_interface, string* err); + string EvaluateCommand(); // XXX move to env, take env ptr + string GetDescription(); + bool LoadDepFile(State* state, DiskInterface* disk_interface, string* err); + + void Dump(); + + const Rule* rule_; + vector inputs_; + vector outputs_; + Env* env_; + + // XXX There are three types of inputs. + // 1) explicit deps, which show up as $in on the command line; + // 2) implicit deps, which the target depends on implicitly (e.g. C headers), + // and changes in them cause the target to rebuild; + // 3) order-only deps, which are needed before the target builds but which + // don't cause the target to rebuild. + // Currently we stuff all of these into inputs_ and keep counts of #2 and #3 + // when we need to compute subsets. This is suboptimal; should think of a + // better representation. (Could make each pointer into a pair of a pointer + // and a type of input, or if memory matters could use the low bits of the + // pointer...) + int implicit_deps_; + int order_only_deps_; + bool is_implicit(int index) { + return index >= ((int)inputs_.size()) - order_only_deps_ - implicit_deps_ && + !is_order_only(index); + } + bool is_order_only(int index) { + return index >= ((int)inputs_.size()) - order_only_deps_; + } +}; + +#endif // NINJA_GRAPH_H_ diff --git a/src/graphviz.h b/src/graphviz.h index d688a15..eb9ee25 100644 --- a/src/graphviz.h +++ b/src/graphviz.h @@ -1,5 +1,7 @@ #include +// XXX deinline all this code so we don't need this include +#include "graph.h" struct Node; struct GraphViz { diff --git a/src/ninja.cc b/src/ninja.cc index d568267..5602806 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -6,9 +6,10 @@ #include "build.h" #include "build_log.h" -#include "graphviz.h" #include "parsers.h" +#include "graphviz.h" + option options[] = { { "help", no_argument, NULL, 'h' }, { } diff --git a/src/ninja.h b/src/ninja.h index 3ece1ab..26432c3 100644 --- a/src/ninja.h +++ b/src/ninja.h @@ -14,6 +14,11 @@ using namespace std; #include "eval_env.h" #include "hash_map.h" +struct Edge; +struct FileStat; +struct Node; +struct Rule; + int ReadFile(const string& path, string* contents, string* err); struct DiskInterface { @@ -34,106 +39,6 @@ struct RealDiskInterface : public DiskInterface { virtual string ReadFile(const string& path, string* err); }; -struct Node; -struct FileStat { - FileStat(const string& path) : path_(path), mtime_(-1), node_(NULL) {} - void Touch(int mtime); - // Return true if the file exists (mtime_ got a value). - bool Stat(DiskInterface* disk_interface); - - // Return true if we needed to stat. - bool StatIfNecessary(DiskInterface* disk_interface) { - if (status_known()) - return false; - Stat(disk_interface); - return true; - } - - bool exists() const { - assert(status_known()); - return mtime_ != 0; - } - - bool status_known() const { - return mtime_ != -1; - } - - 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_; -}; - -struct Edge; -struct Node { - Node(FileStat* file) : file_(file), dirty_(false), in_edge_(NULL) {} - - bool dirty() const { return dirty_; } - void MarkDirty(); - void MarkDependentsDirty(); - - FileStat* file_; - bool dirty_; - Edge* in_edge_; - vector out_edges_; -}; - -struct Rule { - Rule(const string& name) : name_(name) { } - - bool ParseCommand(const string& command, string* err) { - return command_.Parse(command, err); - } - string name_; - EvalString command_; - EvalString description_; - EvalString depfile_; -}; - -struct State; -struct Edge { - Edge() : rule_(NULL), env_(NULL), implicit_deps_(0), order_only_deps_(0) {} - - void MarkDirty(Node* node); - bool RecomputeDirty(State* state, DiskInterface* disk_interface, string* err); - string EvaluateCommand(); // XXX move to env, take env ptr - string GetDescription(); - bool LoadDepFile(State* state, DiskInterface* disk_interface, string* err); - - void Dump(); - - enum InOut { IN, OUT }; - - const Rule* rule_; - vector inputs_; - vector outputs_; - Env* env_; - - // XXX There are three types of inputs. - // 1) explicit deps, which show up as $in on the command line; - // 2) implicit deps, which the target depends on implicitly (e.g. C headers), - // and changes in them cause the target to rebuild; - // 3) order-only deps, which are needed before the target builds but which - // don't cause the target to rebuild. - // Currently we stuff all of these into inputs_ and keep counts of #2 and #3 - // when we need to compute subsets. This is suboptimal; should think of a - // better representation. (Could make each pointer into a pair of a pointer - // and a type of input, or if memory matters could use the low bits of the - // pointer...) - int implicit_deps_; - int order_only_deps_; - bool is_implicit(int index) { - return index >= ((int)inputs_.size()) - order_only_deps_ - implicit_deps_ && - !is_order_only(index); - } - bool is_order_only(int index) { - return index >= ((int)inputs_.size()) - order_only_deps_; - } -}; - struct StatCache { typedef hash_map Paths; Paths paths_; @@ -141,6 +46,7 @@ struct StatCache { void Dump(); void Reload(); }; + struct State { State(); @@ -151,7 +57,8 @@ struct State { Edge* AddEdge(const Rule* rule); Node* GetNode(const string& path); Node* LookupNode(const string& path); - void AddInOut(Edge* edge, Edge::InOut inout, const string& path); + void AddIn(Edge* edge, const string& path); + void AddOut(Edge* edge, const string& path); StatCache stat_cache_; map rules_; diff --git a/src/ninja_jumble.cc b/src/ninja_jumble.cc index 18faac8..d351a6c 100644 --- a/src/ninja_jumble.cc +++ b/src/ninja_jumble.cc @@ -3,13 +3,14 @@ #include "ninja.h" -#include "build_log.h" - #include #include #include #include +#include "build_log.h" +#include "graph.h" + int ReadFile(const string& path, string* contents, string* err) { FILE* f = fopen(path.c_str(), "r"); if (!f) { @@ -90,141 +91,6 @@ bool RealDiskInterface::MakeDir(const string& path) { } return true; } -void FileStat::Touch(int mtime) { - mtime_ = mtime; - if (node_) - node_->MarkDirty(); -} - -bool FileStat::Stat(DiskInterface* disk_interface) { - mtime_ = disk_interface->Stat(path_); - return mtime_ > 0; -} - -void Node::MarkDirty() { - if (dirty_) - return; // We already know. - - dirty_ = true; - MarkDependentsDirty(); -} - -void Node::MarkDependentsDirty() { - for (vector::iterator i = out_edges_.begin(); i != out_edges_.end(); ++i) - (*i)->MarkDirty(this); -} - -bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface, - string* err) { - bool dirty = false; - - if (!rule_->depfile_.empty()) { - if (!LoadDepFile(state, disk_interface, err)) - return false; - } - - time_t most_recent_input = 1; - for (vector::iterator i = inputs_.begin(); i != inputs_.end(); ++i) { - if ((*i)->file_->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. - // But it's ok for implicit deps to be missing. - if (!is_implicit(i - inputs_.begin())) - (*i)->dirty_ = !(*i)->file_->exists(); - } - } - - if (is_order_only(i - inputs_.begin())) { - // Order-only deps only make us dirty if they're missing. - if (!(*i)->file_->exists()) - dirty = true; - continue; - } - - // If a regular input is dirty (or missing), we're dirty. - // Otherwise consider mtime. - if ((*i)->dirty_) { - dirty = true; - } else { - if ((*i)->file_->mtime_ > most_recent_input) - most_recent_input = (*i)->file_->mtime_; - } - } - - string command = EvaluateCommand(); - - assert(!outputs_.empty()); - for (vector::iterator i = outputs_.begin(); i != outputs_.end(); ++i) { - // We may have other outputs, that our input-recursive traversal hasn't hit - // yet (or never will). Stat them if we haven't already. - (*i)->file_->StatIfNecessary(disk_interface); - - // Output is dirty if we're dirty, we're missing the output, - // or if it's older than the most recent input mtime. - if (dirty || !(*i)->file_->exists() || - (*i)->file_->mtime_ < most_recent_input) { - (*i)->dirty_ = true; - } else { - // May also be dirty due to the command changing since the last build. - BuildLog::LogEntry* entry; - if (state->build_log_ && - (entry = state->build_log_->LookupByOutput((*i)->file_->path_))) { - if (command != entry->command) - (*i)->dirty_ = true; - } - } - } - return true; -} - -void Edge::MarkDirty(Node* node) { - if (rule_ == &State::kPhonyRule) - return; - - vector::iterator i = find(inputs_.begin(), inputs_.end(), node); - if (i == inputs_.end()) - return; - if (i - inputs_.begin() >= ((int)inputs_.size()) - order_only_deps_) - return; // Order-only deps don't cause us to become dirty. - for (i = outputs_.begin(); i != outputs_.end(); ++i) - (*i)->MarkDirty(); -} - -struct EdgeEnv : public Env { - EdgeEnv(Edge* edge) : edge_(edge) {} - virtual string LookupVariable(const string& var) { - string result; - if (var == "in") { - int explicit_deps = edge_->inputs_.size() - edge_->implicit_deps_ - - edge_->order_only_deps_; - for (vector::iterator i = edge_->inputs_.begin(); - i != edge_->inputs_.end() && explicit_deps; ++i, --explicit_deps) { - if (!result.empty()) - result.push_back(' '); - result.append((*i)->file_->path_); - } - } else if (var == "out") { - result = edge_->outputs_[0]->file_->path_; - } else if (edge_->env_) { - return edge_->env_->LookupVariable(var); - } - return result; - } - Edge* edge_; -}; - -string Edge::EvaluateCommand() { - EdgeEnv env(this); - return rule_->command_.Evaluate(&env); -} - -string Edge::GetDescription() { - EdgeEnv env(this); - return rule_->description_.Evaluate(&env); -} FileStat* StatCache::GetFile(const string& path) { Paths::iterator i = paths_.find(path); @@ -248,65 +114,6 @@ void StatCache::Dump() { } } -#include "parsers.h" - -bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface, string* err) { - EdgeEnv env(this); - string path = rule_->depfile_.Evaluate(&env); - - string content = disk_interface->ReadFile(path, err); - if (!err->empty()) - return false; - if (content.empty()) - return true; - - MakefileParser makefile; - if (!makefile.Parse(content, err)) - return false; - - // Check that this depfile matches our output. - if (outputs_.size() != 1) { - *err = "expected only one output"; - return false; - } - if (outputs_[0]->file_->path_ != makefile.out_) { - *err = "expected makefile to mention '" + outputs_[0]->file_->path_ + "', " - "got '" + makefile.out_ + "'"; - return false; - } - - // Add all its in-edges. - for (vector::iterator i = makefile.ins_.begin(); - i != makefile.ins_.end(); ++i) { - Node* node = state->GetNode(*i); - for (vector::iterator j = inputs_.begin(); j != inputs_.end(); ++j) { - if (*j == node) { - node = NULL; - break; - } - } - if (node) { - inputs_.insert(inputs_.end() - order_only_deps_, node); - node->out_edges_.push_back(this); - ++implicit_deps_; - } - } - - return true; -} - -void Edge::Dump() { - printf("[ "); - for (vector::iterator i = inputs_.begin(); i != inputs_.end(); ++i) { - printf("%s ", (*i)->file_->path_.c_str()); - } - printf("--%s-> ", rule_->name_.c_str()); - for (vector::iterator i = outputs_.begin(); i != outputs_.end(); ++i) { - printf("%s ", (*i)->file_->path_.c_str()); - } - printf("]\n"); -} - const Rule State::kPhonyRule("phony"); State::State() : build_log_(NULL) { @@ -348,19 +155,20 @@ Node* State::GetNode(const string& path) { return file->node_; } -void State::AddInOut(Edge* edge, Edge::InOut inout, const string& path) { +void State::AddIn(Edge* edge, const string& path) { Node* node = GetNode(path); - if (inout == Edge::IN) { - edge->inputs_.push_back(node); - node->out_edges_.push_back(edge); - } else { - edge->outputs_.push_back(node); - if (node->in_edge_) { - fprintf(stderr, "WARNING: multiple rules generate %s. " - "build will not be correct; continuing anyway\n", path.c_str()); - } - node->in_edge_ = edge; + edge->inputs_.push_back(node); + node->out_edges_.push_back(edge); +} + +void State::AddOut(Edge* edge, const string& path) { + Node* node = GetNode(path); + edge->outputs_.push_back(node); + if (node->in_edge_) { + fprintf(stderr, "WARNING: multiple rules generate %s. " + "build will not be correct; continuing anyway\n", path.c_str()); } + node->in_edge_ = edge; } bool EvalString::Parse(const string& input, string* err) { diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 12ba93c..0c992e9 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -31,9 +31,9 @@ TEST(State, Basic) { ASSERT_EQ("", err); state.AddRule(rule); Edge* edge = state.AddEdge(rule); - state.AddInOut(edge, Edge::IN, "in1"); - state.AddInOut(edge, Edge::IN, "in2"); - state.AddInOut(edge, Edge::OUT, "out"); + state.AddIn(edge, "in1"); + state.AddIn(edge, "in2"); + state.AddOut(edge, "out"); EXPECT_EQ("cat in1 in2 > out", edge->EvaluateCommand()); diff --git a/src/parsers.cc b/src/parsers.cc index 7eee93c..f57c3b9 100644 --- a/src/parsers.cc +++ b/src/parsers.cc @@ -5,6 +5,7 @@ #include #include +#include "graph.h" #include "ninja.h" string Token::AsString() const { @@ -460,9 +461,9 @@ bool ManifestParser::ParseEdge(string* err) { Edge* edge = state_->AddEdge(rule); edge->env_ = env; for (vector::iterator i = ins.begin(); i != ins.end(); ++i) - state_->AddInOut(edge, Edge::IN, *i); + state_->AddIn(edge, *i); for (vector::iterator i = outs.begin(); i != outs.end(); ++i) - state_->AddInOut(edge, Edge::OUT, *i); + state_->AddOut(edge, *i); edge->order_only_deps_ = order_only; return true; diff --git a/src/parsers_test.cc b/src/parsers_test.cc index 6fb418d..28ba619 100644 --- a/src/parsers_test.cc +++ b/src/parsers_test.cc @@ -2,6 +2,7 @@ #include +#include "graph.h" #include "ninja.h" struct ParserTest : public testing::Test, diff --git a/src/test.h b/src/test.h index 4d45a2e..405f039 100644 --- a/src/test.h +++ b/src/test.h @@ -1,7 +1,8 @@ -#include "ninja.h" - #include +#include "graph.h" +#include "ninja.h" + struct StateTestWithBuiltinRules : public testing::Test { StateTestWithBuiltinRules(); Node* GetNode(const string& path); -- cgit v0.12