From 79cc221dfe704f547c30bec0f3922639d60f07ce Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Fri, 29 Oct 2010 21:55:12 -0700 Subject: [PATCH] factor into multiple files --- Makefile | 12 +- eval_env.h | 44 ---- ninja.cc | 3 + ninja.h | 441 ------------------------------------ ninja_jumble.cc | 490 ++++++++++++++++++++++++++++++++++++++++ manifest_parser.h => parsers.cc | 114 ++-------- parsers.h | 87 +++++++ 7 files changed, 609 insertions(+), 582 deletions(-) create mode 100644 ninja_jumble.cc rename manifest_parser.h => parsers.cc (80%) create mode 100644 parsers.h diff --git a/Makefile b/Makefile index 3571752..99fcf24 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,13 @@ CC=$(CXX) CXXFLAGS := -Wall -g -all_i_currently_care_about: tags ninja_test +all_i_currently_care_about: ninja_test -.PHONY: -tags: - etags *.cc *.h +OBJS=parsers.o ninja_jumble.o ninja_test: LDFLAGS = -lgtest -lgtest_main -lpthread -ninja_test: ninja_test.o +ninja_test: ninja_test.o $(OBJS) ninja_test.o: ninja_test.cc ninja.h eval_env.h manifest_parser.h -ninja: ninja.o -ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h +ninja: ninja.o $(OBJS) +ninja.o: ninja.cc diff --git a/eval_env.h b/eval_env.h index 09753e6..62f6c04 100644 --- a/eval_env.h +++ b/eval_env.h @@ -14,47 +14,3 @@ struct EvalString { typedef vector > TokenList; TokenList parsed_; }; - -bool EvalString::Parse(const string& input) { - unparsed_ = input; - - 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) { - char c = input[end]; - if (!(('a' <= c && c <= 'z') || c == '_')) - 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; -} - diff --git a/ninja.cc b/ninja.cc index a56336f..cac2ee0 100644 --- a/ninja.cc +++ b/ninja.cc @@ -1,6 +1,9 @@ #include "ninja.h" #include +#include + +#include "parsers.h" option options[] = { { "help", no_argument, NULL, 'h' }, diff --git a/ninja.h b/ninja.h index b941549..596112f 100644 --- a/ninja.h +++ b/ninja.h @@ -23,83 +23,6 @@ struct DiskInterface { bool MakeDirs(const string& path); }; -#include -#include -#include -#include - -string ReadFile(const string& path, string* err) { - FILE* f = fopen(path.c_str(), "r"); - if (!f) { - if (errno != ENOENT) - err->assign(strerror(errno)); - return ""; - } - - string text; - char buf[64 << 10]; - size_t len; - while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { - text.append(buf, len); - } - if (ferror(f)) { - err->assign(strerror(errno)); - text = ""; - } - fclose(f); - return text; -} - -int DiskInterface::Stat(const string& path) { - struct stat st; - if (stat(path.c_str(), &st) < 0) { - if (errno == ENOENT) { - return 0; - } else { - fprintf(stderr, "stat(%s): %s\n", path.c_str(), strerror(errno)); - return -1; - } - } - - return st.st_mtime; - return true; -} - -string DirName(const string& path) { - string::size_type slash_pos = path.rfind('/'); - if (slash_pos == string::npos) - return ""; // Nothing to do. - return path.substr(0, slash_pos); -} - -bool DiskInterface::MakeDirs(const string& path) { - string dir = DirName(path); - if (dir.empty()) - return true; // Reached root; assume it's there. - int mtime = Stat(dir); - if (mtime < 0) - return false; // Error. - if (mtime > 0) - return true; // Exists already; we're done. - - // Directory doesn't exist. Try creating its parent first. - bool success = MakeDirs(dir); - if (!success) - return false; - return MakeDir(dir); -} - -string DiskInterface::ReadFile(const string& path, string* err) { - return ::ReadFile(path, err); -} - -bool DiskInterface::MakeDir(const string& path) { - if (mkdir(path.c_str(), 0777) < 0) { - fprintf(stderr, "mkdir(%s): %s\n", path.c_str(), strerror(errno)); - return false; - } - return true; -} struct Node; struct FileStat { @@ -177,99 +100,6 @@ struct Edge { int implicit_deps_; // Count on the end of the inputs list. }; -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 { - (*i)->dirty_ = !(*i)->file_->exists(); - } - } - if ((*i)->dirty_) - dirty = true; - else if ((*i)->file_->mtime_ > most_recent_input) - most_recent_input = (*i)->file_->mtime_; - } - - assert(!outputs_.empty()); - for (vector::iterator i = outputs_.begin(); i != outputs_.end(); ++i) { - assert((*i)->file_->status_known()); - if (dirty || (*i)->file_->mtime_ < most_recent_input) { - (*i)->dirty_ = true; - } - } - return true; -} - -void Edge::MarkDirty(Node* node) { - vector::iterator i = find(inputs_.begin(), inputs_.end(), node); - if (i == inputs_.end()) - return; - for (i = outputs_.begin(); i != outputs_.end(); ++i) - (*i)->MarkDirty(); -} - -struct EdgeEnv : public EvalString::Env { - EdgeEnv(Edge* edge) : edge_(edge) {} - virtual string Evaluate(const string& var) { - string result; - if (var == "@in") { - int explicit_deps = edge_->inputs_.size() - edge_->implicit_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_->Evaluate(var); - } - return result; - } - Edge* edge_; -}; - -string Edge::EvaluateCommand() { - EdgeEnv env(this); - return rule_->command_.Evaluate(&env); -} - struct StatCache { typedef map Paths; Paths paths_; @@ -277,29 +107,6 @@ struct StatCache { void Dump(); void Reload(); }; - -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; -} - -#include - -void StatCache::Dump() { - for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) { - FileStat* file = i->second; - printf("%s %s\n", - file->path_.c_str(), - file->status_known() - ? (file->node_->dirty_ ? "dirty" : "clean") - : "unknown"); - } -} - struct State : public EvalString::Env { StatCache stat_cache_; map rules_; @@ -320,113 +127,6 @@ struct State : public EvalString::Env { void AddBinding(const string& key, const string& val); }; -#include "manifest_parser.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_.push_back(node); - node->out_edges_.push_back(this); - ++implicit_deps_; - } - } - - return true; -} - -string State::Evaluate(const string& var) { - if (var.size() > 1 && var[0] == '$') { - map::iterator i = env_.find(var.substr(1)); - if (i != env_.end()) - return i->second; - } - return ""; -} - - -Rule* State::LookupRule(const string& rule_name) { - map::iterator i = rules_.find(rule_name); - if (i == rules_.end()) - return NULL; - return i->second; -} - -void State::AddRule(Rule* rule) { - assert(LookupRule(rule->name_) == NULL); - rules_[rule->name_] = rule; -} - -Edge* State::AddEdge(Rule* rule) { - Edge* edge = new Edge(); - edge->rule_ = rule; - edge->env_ = this; - edges_.push_back(edge); - return edge; -} - -Node* State::LookupNode(const string& path) { - FileStat* file = stat_cache_.GetFile(path); - if (!file->node_) - return NULL; - return file->node_; -} - -Node* State::GetNode(const string& path) { - FileStat* file = stat_cache_.GetFile(path); - if (!file->node_) - file->node_ = new Node(file); - return file->node_; -} - -void State::AddInOut(Edge* edge, Edge::InOut inout, 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); - assert(node->in_edge_ == NULL); - node->in_edge_ = edge; - } -} - -void State::AddBinding(const string& key, const string& val) { - env_[key] = val; -} - struct Plan { explicit Plan(State* state) : state_(state) {} @@ -446,111 +146,12 @@ private: Plan(const Plan&); }; -Node* Plan::AddTarget(const string& path, string* err) { - Node* node = state_->GetNode(path); - AddTarget(node, err); - return node; -} -bool Plan::AddTarget(Node* node, string* err) { - Edge* edge = node->in_edge_; - if (!edge) { // Leaf node. - if (node->dirty_) { - *err = "'" + node->file_->path_ + "' missing and no known rule to make it"; - return false; - } - return false; - } - - assert(edge); - if (!node->dirty()) - return false; - - want_.insert(node); - - bool awaiting_inputs = false; - for (vector::iterator i = edge->inputs_.begin(); - i != edge->inputs_.end(); ++i) { - if (AddTarget(*i, err)) - awaiting_inputs = true; - else if (err && !err->empty()) - return false; - } - - if (!awaiting_inputs) - ready_.push(edge); - - return true; -} - -Edge* Plan::FindWork() { - if (ready_.empty()) - return NULL; - Edge* edge = ready_.front(); - ready_.pop(); - return edge; -} - -void Plan::EdgeFinished(Edge* edge) { - // Check off any nodes we were waiting for with this edge. - for (vector::iterator i = edge->outputs_.begin(); - i != edge->outputs_.end(); ++i) { - set::iterator j = want_.find(*i); - if (j != want_.end()) { - NodeFinished(*j); - want_.erase(j); - } - } -} - -void Plan::NodeFinished(Node* node) { - // See if we we want any edges from this node. - for (vector::iterator i = node->out_edges_.begin(); - i != node->out_edges_.end(); ++i) { - // See if we want any outputs from this edge. - for (vector::iterator j = (*i)->outputs_.begin(); - j != (*i)->outputs_.end(); ++j) { - if (want_.find(*j) != want_.end()) { - // See if the edge is ready. - // XXX just track dirty counts. - // XXX may double-enqueue edge. - bool ready = true; - for (vector::iterator k = (*i)->inputs_.begin(); - k != (*i)->inputs_.end(); ++k) { - if ((*k)->dirty()) { - ready = false; - break; - } - } - if (ready) - ready_.push(*i); - break; - } - } - } -} - struct Shell { virtual ~Shell() {} virtual bool RunCommand(Edge* edge); }; -bool Shell::RunCommand(Edge* edge) { - string err; - string command = edge->EvaluateCommand(); - printf(" %s\n", command.c_str()); - int ret = system(command.c_str()); - if (WIFEXITED(ret)) { - int exit = WEXITSTATUS(ret); - if (exit == 0) - return true; - err = "nonzero exit status"; - } else { - err = "something else went wrong"; - } - return false; -} - struct Builder { Builder(State* state) : state_(state), plan_(state), disk_interface_(&default_disk_interface_) {} @@ -582,45 +183,3 @@ struct Builder { DiskInterface default_disk_interface_; DiskInterface* disk_interface_; }; - -bool Builder::Build(Shell* shell, string* err) { - if (plan_.want_.empty()) { - *err = "no work to do"; - return true; - } - - Edge* edge = plan_.FindWork(); - if (!edge) { - *err = "unable to find work"; - return false; - } - - do { - // Create directories necessary for outputs. - for (vector::iterator i = edge->outputs_.begin(); - i != edge->outputs_.end(); ++i) { - if (!disk_interface_->MakeDirs((*i)->file_->path_)) - return false; - } - - string command = edge->EvaluateCommand(); - if (!shell->RunCommand(edge)) { - err->assign("command '" + command + "' failed."); - return false; - } - for (vector::iterator i = edge->outputs_.begin(); - i != edge->outputs_.end(); ++i) { - // XXX check that the output actually changed - // XXX just notify node and have it propagate? - (*i)->dirty_ = false; - } - plan_.EdgeFinished(edge); - } while ((edge = plan_.FindWork()) != NULL); - - if (!plan_.want_.empty()) { - *err = "ran out of work"; - return false; - } - - return true; -} diff --git a/ninja_jumble.cc b/ninja_jumble.cc new file mode 100644 index 0000000..5462a5b --- /dev/null +++ b/ninja_jumble.cc @@ -0,0 +1,490 @@ +// This file is all the code that used to be in one file. +// TODO: split into modules, delete this file. + +#include "ninja.h" + +#include +#include +#include +#include + +string ReadFile(const string& path, string* err) { + FILE* f = fopen(path.c_str(), "r"); + if (!f) { + if (errno != ENOENT) + err->assign(strerror(errno)); + return ""; + } + + string text; + char buf[64 << 10]; + size_t len; + while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { + text.append(buf, len); + } + if (ferror(f)) { + err->assign(strerror(errno)); + text = ""; + } + fclose(f); + return text; +} + +int DiskInterface::Stat(const string& path) { + struct stat st; + if (stat(path.c_str(), &st) < 0) { + if (errno == ENOENT) { + return 0; + } else { + fprintf(stderr, "stat(%s): %s\n", path.c_str(), strerror(errno)); + return -1; + } + } + + return st.st_mtime; + return true; +} + +string DirName(const string& path) { + string::size_type slash_pos = path.rfind('/'); + if (slash_pos == string::npos) + return ""; // Nothing to do. + return path.substr(0, slash_pos); +} + +bool DiskInterface::MakeDirs(const string& path) { + string dir = DirName(path); + if (dir.empty()) + return true; // Reached root; assume it's there. + int mtime = Stat(dir); + if (mtime < 0) + return false; // Error. + if (mtime > 0) + return true; // Exists already; we're done. + + // Directory doesn't exist. Try creating its parent first. + bool success = MakeDirs(dir); + if (!success) + return false; + return MakeDir(dir); +} + +string DiskInterface::ReadFile(const string& path, string* err) { + return ::ReadFile(path, err); +} + +bool DiskInterface::MakeDir(const string& path) { + if (mkdir(path.c_str(), 0777) < 0) { + fprintf(stderr, "mkdir(%s): %s\n", path.c_str(), strerror(errno)); + return false; + } + 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 { + (*i)->dirty_ = !(*i)->file_->exists(); + } + } + if ((*i)->dirty_) + dirty = true; + else if ((*i)->file_->mtime_ > most_recent_input) + most_recent_input = (*i)->file_->mtime_; + } + + assert(!outputs_.empty()); + for (vector::iterator i = outputs_.begin(); i != outputs_.end(); ++i) { + assert((*i)->file_->status_known()); + if (dirty || (*i)->file_->mtime_ < most_recent_input) { + (*i)->dirty_ = true; + } + } + return true; +} + +void Edge::MarkDirty(Node* node) { + vector::iterator i = find(inputs_.begin(), inputs_.end(), node); + if (i == inputs_.end()) + return; + for (i = outputs_.begin(); i != outputs_.end(); ++i) + (*i)->MarkDirty(); +} + +struct EdgeEnv : public EvalString::Env { + EdgeEnv(Edge* edge) : edge_(edge) {} + virtual string Evaluate(const string& var) { + string result; + if (var == "@in") { + int explicit_deps = edge_->inputs_.size() - edge_->implicit_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_->Evaluate(var); + } + return result; + } + Edge* edge_; +}; + +string Edge::EvaluateCommand() { + EdgeEnv env(this); + return rule_->command_.Evaluate(&env); +} + + +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; +} + +#include + +void StatCache::Dump() { + for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) { + FileStat* file = i->second; + printf("%s %s\n", + file->path_.c_str(), + file->status_known() + ? (file->node_->dirty_ ? "dirty" : "clean") + : "unknown"); + } +} + +#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_.push_back(node); + node->out_edges_.push_back(this); + ++implicit_deps_; + } + } + + return true; +} + +string State::Evaluate(const string& var) { + if (var.size() > 1 && var[0] == '$') { + map::iterator i = env_.find(var.substr(1)); + if (i != env_.end()) + return i->second; + } + return ""; +} + + +Rule* State::LookupRule(const string& rule_name) { + map::iterator i = rules_.find(rule_name); + if (i == rules_.end()) + return NULL; + return i->second; +} + +void State::AddRule(Rule* rule) { + assert(LookupRule(rule->name_) == NULL); + rules_[rule->name_] = rule; +} + +Edge* State::AddEdge(Rule* rule) { + Edge* edge = new Edge(); + edge->rule_ = rule; + edge->env_ = this; + edges_.push_back(edge); + return edge; +} + +Node* State::LookupNode(const string& path) { + FileStat* file = stat_cache_.GetFile(path); + if (!file->node_) + return NULL; + return file->node_; +} + +Node* State::GetNode(const string& path) { + FileStat* file = stat_cache_.GetFile(path); + if (!file->node_) + file->node_ = new Node(file); + return file->node_; +} + +void State::AddInOut(Edge* edge, Edge::InOut inout, 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); + assert(node->in_edge_ == NULL); + node->in_edge_ = edge; + } +} + +void State::AddBinding(const string& key, const string& val) { + env_[key] = val; +} + +Node* Plan::AddTarget(const string& path, string* err) { + Node* node = state_->GetNode(path); + AddTarget(node, err); + return node; +} +bool Plan::AddTarget(Node* node, string* err) { + Edge* edge = node->in_edge_; + if (!edge) { // Leaf node. + if (node->dirty_) { + *err = "'" + node->file_->path_ + "' missing and no known rule to make it"; + return false; + } + return false; + } + + assert(edge); + if (!node->dirty()) + return false; + + want_.insert(node); + + bool awaiting_inputs = false; + for (vector::iterator i = edge->inputs_.begin(); + i != edge->inputs_.end(); ++i) { + if (AddTarget(*i, err)) + awaiting_inputs = true; + else if (err && !err->empty()) + return false; + } + + if (!awaiting_inputs) + ready_.push(edge); + + return true; +} + +Edge* Plan::FindWork() { + if (ready_.empty()) + return NULL; + Edge* edge = ready_.front(); + ready_.pop(); + return edge; +} + +void Plan::EdgeFinished(Edge* edge) { + // Check off any nodes we were waiting for with this edge. + for (vector::iterator i = edge->outputs_.begin(); + i != edge->outputs_.end(); ++i) { + set::iterator j = want_.find(*i); + if (j != want_.end()) { + NodeFinished(*j); + want_.erase(j); + } + } +} + +void Plan::NodeFinished(Node* node) { + // See if we we want any edges from this node. + for (vector::iterator i = node->out_edges_.begin(); + i != node->out_edges_.end(); ++i) { + // See if we want any outputs from this edge. + for (vector::iterator j = (*i)->outputs_.begin(); + j != (*i)->outputs_.end(); ++j) { + if (want_.find(*j) != want_.end()) { + // See if the edge is ready. + // XXX just track dirty counts. + // XXX may double-enqueue edge. + bool ready = true; + for (vector::iterator k = (*i)->inputs_.begin(); + k != (*i)->inputs_.end(); ++k) { + if ((*k)->dirty()) { + ready = false; + break; + } + } + if (ready) + ready_.push(*i); + break; + } + } + } +} + +bool Shell::RunCommand(Edge* edge) { + string err; + string command = edge->EvaluateCommand(); + printf(" %s\n", command.c_str()); + int ret = system(command.c_str()); + if (WIFEXITED(ret)) { + int exit = WEXITSTATUS(ret); + if (exit == 0) + return true; + err = "nonzero exit status"; + } else { + err = "something else went wrong"; + } + return false; +} + + +bool Builder::Build(Shell* shell, string* err) { + if (plan_.want_.empty()) { + *err = "no work to do"; + return true; + } + + Edge* edge = plan_.FindWork(); + if (!edge) { + *err = "unable to find work"; + return false; + } + + do { + // Create directories necessary for outputs. + for (vector::iterator i = edge->outputs_.begin(); + i != edge->outputs_.end(); ++i) { + if (!disk_interface_->MakeDirs((*i)->file_->path_)) + return false; + } + + string command = edge->EvaluateCommand(); + if (!shell->RunCommand(edge)) { + err->assign("command '" + command + "' failed."); + return false; + } + for (vector::iterator i = edge->outputs_.begin(); + i != edge->outputs_.end(); ++i) { + // XXX check that the output actually changed + // XXX just notify node and have it propagate? + (*i)->dirty_ = false; + } + plan_.EdgeFinished(edge); + } while ((edge = plan_.FindWork()) != NULL); + + if (!plan_.want_.empty()) { + *err = "ran out of work"; + return false; + } + + return true; +} + +bool EvalString::Parse(const string& input) { + unparsed_ = input; + + 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) { + char c = input[end]; + if (!(('a' <= c && c <= 'z') || c == '_')) + 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; +} + diff --git a/manifest_parser.h b/parsers.cc similarity index 80% rename from manifest_parser.h rename to parsers.cc index 577a7fb..39ff424 100644 --- a/manifest_parser.h +++ b/parsers.cc @@ -1,75 +1,30 @@ +#include "parsers.h" + +#include #include #include #include -struct Token { - enum Type { - NONE, - UNKNOWN, - IDENT, - RULE, - BUILD, - NEWLINE, - EQUALS, - COLON, - INDENT, - OUTDENT, - TEOF - }; - explicit Token(Type type) : type_(type) {} - - void Clear() { type_ = NONE; extra_.clear(); } - string AsString() const { - switch (type_) { - case IDENT: return "'" + extra_ + "'"; - case UNKNOWN: return "unknown '" + extra_ + "'"; - case RULE: return "'rule'"; - case BUILD: return "'build'"; - case NEWLINE: return "newline"; - case EQUALS: return "'='"; - case COLON: return "':'"; - case TEOF: return "eof"; - case INDENT: return "indenting in"; - case OUTDENT: return "indenting out"; - case NONE: - default: - assert(false); - return ""; - } +#include "ninja.h" + +string Token::AsString() const { + switch (type_) { + case IDENT: return "'" + extra_ + "'"; + case UNKNOWN: return "unknown '" + extra_ + "'"; + case RULE: return "'rule'"; + case BUILD: return "'build'"; + case NEWLINE: return "newline"; + case EQUALS: return "'='"; + case COLON: return "':'"; + case TEOF: return "eof"; + case INDENT: return "indenting in"; + case OUTDENT: return "indenting out"; + case NONE: + default: + assert(false); + return ""; } - - Type type_; - const char* pos_; - string extra_; -}; - -struct Tokenizer { - Tokenizer() - : token_(Token::NONE), line_number_(1), - last_indent_(0), cur_indent_(-1) {} - - void Start(const char* start, const char* end); - bool Error(const string& message, string* err); - - const Token& token() const { return token_; } - - void SkipWhitespace(bool newline=false); - bool Newline(string* err); - bool ExpectToken(Token::Type expected, string* err); - bool ReadIdent(string* out); - bool ReadToNewline(string* text, string* err); - - Token::Type PeekToken(); - void ConsumeToken(); - - const char* cur_; - const char* end_; - - const char* cur_line_; - Token token_; - int line_number_; - int last_indent_, cur_indent_; -}; +} void Tokenizer::Start(const char* start, const char* end) { cur_line_ = cur_ = start; @@ -230,14 +185,6 @@ void Tokenizer::ConsumeToken() { token_.Clear(); } -struct MakefileParser { - bool Parse(const string& input, string* err); - - Tokenizer tokenizer_; - string out_; - vector ins_; -}; - bool MakefileParser::Parse(const string& input, string* err) { tokenizer_.Start(input.data(), input.data() + input.size()); @@ -258,21 +205,8 @@ bool MakefileParser::Parse(const string& input, string* err) { return true; } -struct ManifestParser { - ManifestParser(State* state) : state_(state) {} - bool Load(const string& filename, string* err); - bool Parse(const string& input, string* err); - - bool ParseRule(string* err); - bool ParseLet(string* key, string* val, string* err); - bool ParseEdge(string* err); - - string ExpandFile(const string& file); - - State* state_; - Tokenizer tokenizer_; - string builddir_; -}; +// XXX refactor. +extern string ReadFile(const string& path, string* err); bool ManifestParser::Load(const string& filename, string* err) { string input = ReadFile(filename, err); diff --git a/parsers.h b/parsers.h new file mode 100644 index 0000000..d7d35a3 --- /dev/null +++ b/parsers.h @@ -0,0 +1,87 @@ +#ifndef NINJA_PARSERS_H_ +#define NINJA_PARSERS_H_ + +#include +#include + +using namespace std; + +struct Token { + enum Type { + NONE, + UNKNOWN, + IDENT, + RULE, + BUILD, + NEWLINE, + EQUALS, + COLON, + INDENT, + OUTDENT, + TEOF + }; + explicit Token(Type type) : type_(type) {} + + void Clear() { type_ = NONE; extra_.clear(); } + string AsString() const; + + Type type_; + const char* pos_; + string extra_; +}; + +struct Tokenizer { + Tokenizer() + : token_(Token::NONE), line_number_(1), + last_indent_(0), cur_indent_(-1) {} + + void Start(const char* start, const char* end); + bool Error(const string& message, string* err); + + const Token& token() const { return token_; } + + void SkipWhitespace(bool newline=false); + bool Newline(string* err); + bool ExpectToken(Token::Type expected, string* err); + bool ReadIdent(string* out); + bool ReadToNewline(string* text, string* err); + + Token::Type PeekToken(); + void ConsumeToken(); + + const char* cur_; + const char* end_; + + const char* cur_line_; + Token token_; + int line_number_; + int last_indent_, cur_indent_; +}; + +struct MakefileParser { + bool Parse(const string& input, string* err); + + Tokenizer tokenizer_; + string out_; + vector ins_; +}; + +struct State; + +struct ManifestParser { + ManifestParser(State* state) : state_(state) {} + bool Load(const string& filename, string* err); + bool Parse(const string& input, string* err); + + bool ParseRule(string* err); + bool ParseLet(string* key, string* val, string* err); + bool ParseEdge(string* err); + + string ExpandFile(const string& file); + + State* state_; + Tokenizer tokenizer_; + string builddir_; +}; + +#endif // NINJA_PARSERS_H_ -- 2.7.4