refactor ninja main() into a struct with methods
authorEvan Martin <martine@danga.com>
Fri, 24 May 2013 23:32:49 +0000 (16:32 -0700)
committerEvan Martin <martine@danga.com>
Fri, 24 May 2013 23:33:50 +0000 (16:33 -0700)
This removes the ugly Globals struct, and the ResetState() hack.
This also eliminates the only goto!

src/ninja.cc

index 4d0c16a..cd34b1a 100644 (file)
@@ -66,29 +66,76 @@ struct Options {
   const Tool* tool;
 };
 
-/// Global information passed into subtools.
-struct Globals {
-  Globals() : state(new State()) {}
-  ~Globals() {
-    delete state;
-  }
-
-  /// Deletes and recreates state so it is empty.
-  void ResetState() {
-    delete state;
-    state = new State();
-  }
+/// The Ninja main() loads up a series of data structures; various tools need
+/// to poke into these, so store them as fields on an object.
+struct NinjaMain {
+  NinjaMain(const char* ninja_command, const BuildConfig& config) :
+      ninja_command_(ninja_command), config_(config) {}
 
   /// Command line used to run Ninja.
-  const char* ninja_command;
+  const char* ninja_command_;
+
   /// Build configuration set from flags (e.g. parallelism).
-  BuildConfig* config;
-  /// Loaded state (rules, nodes). This is a pointer so it can be reset.
-  State* state;
-};
+  const BuildConfig& config_;
+
+  /// Loaded state (rules, nodes).
+  State state_;
+
+  /// Functions for accesssing the disk.
+  RealDiskInterface disk_interface_;
+
+  /// The build directory, used for storing the build log etc.
+  string build_dir_;
+
+  BuildLog build_log_;
+  DepsLog deps_log_;
+
+  /// The type of functions that are the entry points to tools (subcommands).
+  typedef int (NinjaMain::*ToolFunc)(int, char**);
+
+  /// Get the Node for a given command-line path, handling features like
+  /// spell correction.
+  Node* CollectTarget(const char* cpath, string* err);
+
+  /// CollectTarget for all command-line arguments, filling in \a targets.
+  bool CollectTargetsFromArgs(int argc, char* argv[],
+                              vector<Node*>* targets, string* err);
+
+  // The various subcommands, run via "-t XXX".
+  int ToolGraph(int argc, char* argv[]);
+  int ToolQuery(int argc, char* argv[]);
+  int ToolBrowse(int argc, char* argv[]);
+  int ToolMSVC(int argc, char* argv[]);
+  int ToolTargets(int argc, char* argv[]);
+  int ToolCommands(int argc, char* argv[]);
+  int ToolClean(int argc, char* argv[]);
+  int ToolCompilationDatabase(int argc, char* argv[]);
+  int ToolUrtle(int argc, char** argv);
 
-/// The type of functions that are the entry points to tools (subcommands).
-typedef int (*ToolFunc)(Globals*, int, char**);
+  /// Open the build log.
+  /// @return false on error.
+  bool OpenBuildLog();
+
+  /// Open the deps log: load it, then open for writing.
+  /// @return false on error.
+  bool OpenDepsLog();
+
+  /// Ensure the build directory exists, creating it if necessary.
+  /// @return false on error.
+  bool EnsureBuildDirExists();
+
+  /// Rebuild the manifest, if necessary.
+  /// Fills in \a err on error.
+  /// @return true if the manifest was rebuilt.
+  bool RebuildManifest(const char* input_file, string* err);
+
+  /// Build the targets listed on the command line.
+  /// @return an exit code.
+  int RunBuild(int argc, char** argv);
+
+  /// Dump the output requested by '-d stats'.
+  void DumpMetrics();
+};
 
 /// Subtools, accessible via "-t foo".
 struct Tool {
@@ -105,10 +152,13 @@ struct Tool {
 
     /// Run after loading build.ninja.
     RUN_AFTER_LOAD,
+
+    /// Run after loading the build/deps logs.
+    RUN_AFTER_LOGS,
   } when;
 
   /// Implementation of the tool.
-  ToolFunc func;
+  NinjaMain::ToolFunc func;
 };
 
 /// Print usage information.
@@ -162,20 +212,21 @@ struct RealFileReader : public ManifestParser::FileReader {
 
 /// Rebuild the build manifest, if necessary.
 /// Returns true if the manifest was rebuilt.
-bool RebuildManifest(Builder* builder, const char* input_file, string* err) {
+bool NinjaMain::RebuildManifest(const char* input_file, string* err) {
   string path = input_file;
   if (!CanonicalizePath(&path, err))
     return false;
-  Node* node = builder->state_->LookupNode(path);
+  Node* node = state_.LookupNode(path);
   if (!node)
     return false;
 
-  if (!builder->AddTarget(node, err))
+  Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_);
+  if (!builder.AddTarget(node, err))
     return false;
 
-  if (builder->AlreadyUpToDate())
+  if (builder.AlreadyUpToDate())
     return false;  // Not an error, but we didn't rebuild.
-  if (!builder->Build(err))
+  if (!builder.Build(err))
     return false;
 
   // The manifest was only rebuilt if it is now dirty (it may have been cleaned
@@ -183,7 +234,7 @@ bool RebuildManifest(Builder* builder, const char* input_file, string* err) {
   return node->dirty();
 }
 
-Node* CollectTarget(State* state, const char* cpath, string* err) {
+Node* NinjaMain::CollectTarget(const char* cpath, string* err) {
   string path = cpath;
   if (!CanonicalizePath(&path, err))
     return NULL;
@@ -195,7 +246,7 @@ Node* CollectTarget(State* state, const char* cpath, string* err) {
     first_dependent = true;
   }
 
-  Node* node = state->LookupNode(path);
+  Node* node = state_.LookupNode(path);
   if (node) {
     if (first_dependent) {
       if (node->out_edges().empty()) {
@@ -218,7 +269,7 @@ Node* CollectTarget(State* state, const char* cpath, string* err) {
     } else if (path == "help") {
       *err += ", did you mean 'ninja -h'?";
     } else {
-      Node* suggestion = state->SpellcheckNode(path);
+      Node* suggestion = state_.SpellcheckNode(path);
       if (suggestion) {
         *err += ", did you mean '" + suggestion->path() + "'?";
       }
@@ -227,15 +278,15 @@ Node* CollectTarget(State* state, const char* cpath, string* err) {
   }
 }
 
-bool CollectTargetsFromArgs(State* state, int argc, char* argv[],
-                            vector<Node*>* targets, string* err) {
+bool NinjaMain::CollectTargetsFromArgs(int argc, char* argv[],
+                                       vector<Node*>* targets, string* err) {
   if (argc == 0) {
-    *targets = state->DefaultNodes(err);
+    *targets = state_.DefaultNodes(err);
     return err->empty();
   }
 
   for (int i = 0; i < argc; ++i) {
-    Node* node = CollectTarget(state, argv[i], err);
+    Node* node = CollectTarget(argv[i], err);
     if (node == NULL)
       return false;
     targets->push_back(node);
@@ -243,10 +294,10 @@ bool CollectTargetsFromArgs(State* state, int argc, char* argv[],
   return true;
 }
 
-int ToolGraph(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolGraph(int argc, char* argv[]) {
   vector<Node*> nodes;
   string err;
-  if (!CollectTargetsFromArgs(globals->state, argc, argv, &nodes, &err)) {
+  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
     Error("%s", err.c_str());
     return 1;
   }
@@ -260,14 +311,15 @@ int ToolGraph(Globals* globals, int argc, char* argv[]) {
   return 0;
 }
 
-int ToolQuery(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolQuery(int argc, char* argv[]) {
   if (argc == 0) {
     Error("expected a target to query");
     return 1;
   }
+
   for (int i = 0; i < argc; ++i) {
     string err;
-    Node* node = CollectTarget(globals->state, argv[i], &err);
+    Node* node = CollectTarget(argv[i], &err);
     if (!node) {
       Error("%s", err.c_str());
       return 1;
@@ -298,19 +350,19 @@ int ToolQuery(Globals* globals, int argc, char* argv[]) {
 }
 
 #if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
-int ToolBrowse(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolBrowse(int argc, char* argv[]) {
   if (argc < 1) {
     Error("expected a target to browse");
     return 1;
   }
-  RunBrowsePython(globals->state, globals->ninja_command, argv[0]);
+  RunBrowsePython(&state_, ninja_command_, argv[0]);
   // If we get here, the browse failed.
   return 1;
 }
 #endif  // _WIN32
 
 #if defined(_WIN32)
-int ToolMSVC(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolMSVC(int argc, char* argv[]) {
   // Reset getopt: push one argument onto the front of argv, reset optind.
   argc++;
   argv--;
@@ -385,7 +437,7 @@ int ToolTargetsList(State* state) {
   return 0;
 }
 
-int ToolTargets(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolTargets(int argc, char* argv[]) {
   int depth = 1;
   if (argc >= 1) {
     string mode = argv[0];
@@ -394,14 +446,14 @@ int ToolTargets(Globals* globals, int argc, char* argv[]) {
       if (argc > 1)
         rule = argv[1];
       if (rule.empty())
-        return ToolTargetsSourceList(globals->state);
+        return ToolTargetsSourceList(&state_);
       else
-        return ToolTargetsList(globals->state, rule);
+        return ToolTargetsList(&state_, rule);
     } else if (mode == "depth") {
       if (argc > 1)
         depth = atoi(argv[1]);
     } else if (mode == "all") {
-      return ToolTargetsList(globals->state);
+      return ToolTargetsList(&state_);
     } else {
       const char* suggestion =
           SpellcheckString(mode.c_str(), "rule", "depth", "all", NULL);
@@ -416,7 +468,7 @@ int ToolTargets(Globals* globals, int argc, char* argv[]) {
   }
 
   string err;
-  vector<Node*> root_nodes = globals->state->RootNodes(&err);
+  vector<Node*> root_nodes = state_.RootNodes(&err);
   if (err.empty()) {
     return ToolTargetsList(root_nodes, depth, 0);
   } else {
@@ -439,10 +491,10 @@ void PrintCommands(Edge* edge, set<Edge*>* seen) {
     puts(edge->EvaluateCommand().c_str());
 }
 
-int ToolCommands(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolCommands(int argc, char* argv[]) {
   vector<Node*> nodes;
   string err;
-  if (!CollectTargetsFromArgs(globals->state, argc, argv, &nodes, &err)) {
+  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
     Error("%s", err.c_str());
     return 1;
   }
@@ -454,7 +506,7 @@ int ToolCommands(Globals* globals, int argc, char* argv[]) {
   return 0;
 }
 
-int ToolClean(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolClean(int argc, char* argv[]) {
   // The clean tool uses getopt, and expects argv[0] to contain the name of
   // the tool, i.e. "clean".
   argc++;
@@ -492,7 +544,7 @@ int ToolClean(Globals* globals, int argc, char* argv[]) {
     return 1;
   }
 
-  Cleaner cleaner(globals->state, *globals->config);
+  Cleaner cleaner(&state_, config_);
   if (argc >= 1) {
     if (clean_rules)
       return cleaner.CleanRules(argc, argv);
@@ -512,7 +564,7 @@ void EncodeJSONString(const char *str) {
   }
 }
 
-int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolCompilationDatabase(int argc, char* argv[]) {
   bool first = true;
   vector<char> cwd;
 
@@ -526,8 +578,8 @@ int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
   }
 
   putchar('[');
-  for (vector<Edge*>::iterator e = globals->state->edges_.begin();
-       e != globals->state->edges_.end(); ++e) {
+  for (vector<Edge*>::iterator e = state_.edges_.begin();
+       e != state_.edges_.end(); ++e) {
     for (int i = 0; i != argc; ++i) {
       if ((*e)->rule_->name() == argv[i]) {
         if (!first)
@@ -550,7 +602,7 @@ int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
   return 0;
 }
 
-int ToolUrtle(Globals* globals, int argc, char** argv) {
+int NinjaMain::ToolUrtle(int argc, char** argv) {
   // RLE encoded.
   const char* urtle =
 " 13 ,3;2!2;\n8 ,;<11!;\n5 `'<10!(2`'2!\n11 ,6;, `\\. `\\9 .,c13$ec,.\n6 "
@@ -582,26 +634,26 @@ const Tool* ChooseTool(const string& tool_name) {
   static const Tool kTools[] = {
 #if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
     { "browse", "browse dependency graph in a web browser",
-      Tool::RUN_AFTER_LOAD, ToolBrowse },
+      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse },
 #endif
 #if defined(_WIN32)
     { "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)",
-      Tool::RUN_AFTER_FLAGS, ToolMSVC },
+      Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC },
 #endif
     { "clean", "clean built files",
-      Tool::RUN_AFTER_LOAD, ToolClean },
+      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean },
     { "commands", "list all commands required to rebuild given targets",
-      Tool::RUN_AFTER_LOAD, ToolCommands },
+      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands },
     { "graph", "output graphviz dot file for targets",
-      Tool::RUN_AFTER_LOAD, ToolGraph },
+      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph },
     { "query", "show inputs/outputs for a path",
-      Tool::RUN_AFTER_LOAD, ToolQuery },
+      Tool::RUN_AFTER_LOGS, &NinjaMain::ToolQuery },
     { "targets",  "list targets by their rule or depth in the DAG",
-      Tool::RUN_AFTER_LOAD, ToolTargets },
+      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolTargets },
     { "compdb",  "dump JSON compilation database to stdout",
-      Tool::RUN_AFTER_LOAD, ToolCompilationDatabase },
+      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase },
     { "urtle", NULL,
-      Tool::RUN_AFTER_FLAGS, ToolUrtle },
+      Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolUrtle },
     { NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL }
   };
 
@@ -660,16 +712,13 @@ bool DebugEnable(const string& name) {
   }
 }
 
-/// Open the build log.
-/// @return false on error.
-bool OpenBuildLog(BuildLog* build_log, const string& build_dir,
-                  Globals* globals, DiskInterface* disk_interface) {
+bool NinjaMain::OpenBuildLog() {
   string log_path = ".ninja_log";
-  if (!build_dir.empty())
-    log_path = build_dir + "/" + log_path;
+  if (!build_dir_.empty())
+    log_path = build_dir_ + "/" + log_path;
 
   string err;
-  if (!build_log->Load(log_path, &err)) {
+  if (!build_log_.Load(log_path, &err)) {
     Error("loading build log %s: %s", log_path.c_str(), err.c_str());
     return false;
   }
@@ -679,8 +728,8 @@ bool OpenBuildLog(BuildLog* build_log, const string& build_dir,
     err.clear();
   }
 
-  if (!globals->config->dry_run) {
-    if (!build_log->OpenForWrite(log_path, &err)) {
+  if (!config_.dry_run) {
+    if (!build_log_.OpenForWrite(log_path, &err)) {
       Error("opening build log: %s", err.c_str());
       return false;
     }
@@ -691,14 +740,13 @@ bool OpenBuildLog(BuildLog* build_log, const string& build_dir,
 
 /// Open the deps log: load it, then open for writing.
 /// @return false on error.
-bool OpenDepsLog(DepsLog* deps_log, const string& build_dir,
-                 Globals* globals, DiskInterface* disk_interface) {
+bool NinjaMain::OpenDepsLog() {
   string path = ".ninja_deps";
-  if (!build_dir.empty())
-    path = build_dir + "/" + path;
+  if (!build_dir_.empty())
+    path = build_dir_ + "/" + path;
 
   string err;
-  if (!deps_log->Load(path, globals->state, &err)) {
+  if (!deps_log_.Load(path, &state_, &err)) {
     Error("loading deps log %s: %s", path.c_str(), err.c_str());
     return false;
   }
@@ -708,8 +756,8 @@ bool OpenDepsLog(DepsLog* deps_log, const string& build_dir,
     err.clear();
   }
 
-  if (!globals->config->dry_run) {
-    if (!deps_log->OpenForWrite(path, &err)) {
+  if (!config_.dry_run) {
+    if (!deps_log_.OpenForWrite(path, &err)) {
       Error("opening deps log: %s", err.c_str());
       return false;
     }
@@ -718,28 +766,39 @@ bool OpenDepsLog(DepsLog* deps_log, const string& build_dir,
   return true;
 }
 
-
-/// Dump the output requested by '-d stats'.
-void DumpMetrics(Globals* globals) {
+void NinjaMain::DumpMetrics() {
   g_metrics->Report();
 
   printf("\n");
-  int count = (int)globals->state->paths_.size();
-  int buckets = (int)globals->state->paths_.bucket_count();
+  int count = (int)state_.paths_.size();
+  int buckets = (int)state_.paths_.bucket_count();
   printf("path->node hash load %.2f (%d entries / %d buckets)\n",
          count / (double) buckets, count, buckets);
 }
 
-int RunBuild(Builder* builder, int argc, char** argv) {
+bool NinjaMain::EnsureBuildDirExists() {
+  build_dir_ = state_.bindings_.LookupVariable("builddir");
+  if (!build_dir_.empty() && !config_.dry_run) {
+    if (!disk_interface_.MakeDirs(build_dir_ + "/.") && errno != EEXIST) {
+      Error("creating build directory %s: %s",
+            build_dir_.c_str(), strerror(errno));
+      return false;
+    }
+  }
+  return true;
+}
+
+int NinjaMain::RunBuild(int argc, char** argv) {
   string err;
   vector<Node*> targets;
-  if (!CollectTargetsFromArgs(builder->state_, argc, argv, &targets, &err)) {
+  if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) {
     Error("%s", err.c_str());
     return 1;
   }
 
+  Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_);
   for (size_t i = 0; i < targets.size(); ++i) {
-    if (!builder->AddTarget(targets[i], &err)) {
+    if (!builder.AddTarget(targets[i], &err)) {
       if (!err.empty()) {
         Error("%s", err.c_str());
         return 1;
@@ -750,15 +809,15 @@ int RunBuild(Builder* builder, int argc, char** argv) {
     }
   }
 
-  if (builder->AlreadyUpToDate()) {
+  if (builder.AlreadyUpToDate()) {
     printf("ninja: no work to do.\n");
     return 0;
   }
 
-  if (!builder->Build(&err)) {
+  if (!builder.Build(&err)) {
     printf("ninja: build stopped: %s.\n", err.c_str());
     if (err.find("interrupted by user") != string::npos) {
-       return 2;
+      return 2;
     }
     return 1;
   }
@@ -789,15 +848,11 @@ int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
 
 #endif  // _MSC_VER
 
-/// Parse argc/argv for command-line options.
-/// Returns an exit code or -1 if Ninja should continue.
-int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
-              Options* options) {
-  // To reduce the churn of this code, use a reference here; this will
-  // be refactored out in a subsequent change.
-  BuildConfig& config = *config_ptr;
-
-  config.parallelism = GuessParallelism();
+/// Parse argv for command-line options.
+/// Returns an exit code, or -1 if Ninja should continue.
+int ReadFlags(int* argc, char*** argv,
+              Options* options, BuildConfig* config) {
+  config->parallelism = GuessParallelism();
 
   enum { OPT_VERSION = 1 };
   const option kLongOptions[] = {
@@ -823,7 +878,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
         int value = strtol(optarg, &end, 10);
         if (*end != 0 || value <= 0)
           Fatal("invalid -j parameter");
-        config.parallelism = value;
+        config->parallelism = value;
         break;
       }
       case 'k': {
@@ -835,7 +890,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
         // We want to go until N jobs fail, which means we should allow
         // N failures and then stop.  For N <= 0, INT_MAX is close enough
         // to infinite for most sane builds.
-        config.failures_allowed = value > 0 ? value : INT_MAX;
+        config->failures_allowed = value > 0 ? value : INT_MAX;
         break;
       }
       case 'l': {
@@ -843,11 +898,11 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
         double value = strtod(optarg, &end);
         if (end == optarg)
           Fatal("-l parameter not numeric: did you mean -l 0.0?");
-        config.max_load_average = value;
+        config->max_load_average = value;
         break;
       }
       case 'n':
-        config.dry_run = true;
+        config->dry_run = true;
         break;
       case 't':
         options->tool = ChooseTool(optarg);
@@ -855,7 +910,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
           return 0;
         break;
       case 'v':
-        config.verbosity = BuildConfig::VERBOSE;
+        config->verbosity = BuildConfig::VERBOSE;
         break;
       case 'C':
         options->working_dir = optarg;
@@ -865,7 +920,7 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
         return 0;
       case 'h':
       default:
-        Usage(config);
+        Usage(*config);
         return 1;
     }
   }
@@ -875,23 +930,23 @@ int ReadFlags(BuildConfig* config_ptr, int* argc, char*** argv,
   return -1;
 }
 
-int NinjaMain(int argc, char** argv) {
+int real_main(int argc, char** argv) {
   BuildConfig config;
-  Globals globals;
-  globals.ninja_command = argv[0];
-  globals.config = &config;
   Options options = {};
   options.input_file = "build.ninja";
 
   setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
 
-  Tool* tool = NULL;
-  int exit_code = ReadFlags(&config, &argc, &argv, &options);
+  int exit_code = ReadFlags(&argc, &argv, &options, &config);
   if (exit_code >= 0)
     return exit_code;
 
-  if (tool && tool->when == Tool::RUN_AFTER_FLAGS)
-    return tool->func(&globals, argc, argv);
+  if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) {
+    // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed
+    // by other tools.
+    NinjaMain ninja(argv[0], config);
+    return (ninja.*options.tool->func)(argc, argv);
+  }
 
   if (options.working_dir) {
     // The formatting of this string, complete with funny quotes, is
@@ -899,68 +954,57 @@ int NinjaMain(int argc, char** argv) {
     // subsequent commands.
     // Don't print this if a tool is being used, so that tool output
     // can be piped into a file without this string showing up.
-    if (!tool)
+    if (!options.tool)
       printf("ninja: Entering directory `%s'\n", options.working_dir);
     if (chdir(options.working_dir) < 0) {
       Fatal("chdir to '%s' - %s", options.working_dir, strerror(errno));
     }
   }
 
-  bool rebuilt_manifest = false;
-
-reload:
-  RealFileReader file_reader;
-  ManifestParser parser(globals.state, &file_reader);
-  string err;
-  if (!parser.Load(options.input_file, &err)) {
-    Error("%s", err.c_str());
-    return 1;
-  }
-
-  if (tool && tool->when == Tool::RUN_AFTER_LOAD)
-    return tool->func(&globals, argc, argv);
+  // The build can take up to 2 passes: one to rebuild the manifest, then
+  // another to build the desired target.
+  for (int cycle = 0; cycle < 2; ++cycle) {
+    NinjaMain ninja(argv[0], config);
 
-  RealDiskInterface disk_interface;
-
-  // Create the build dir if it doesn't exist.
-  const string build_dir = globals.state->bindings_.LookupVariable("builddir");
-  if (!build_dir.empty() && !config.dry_run) {
-    if (!disk_interface.MakeDirs(build_dir + "/.") &&
-        errno != EEXIST) {
-      Error("creating build directory %s: %s",
-            build_dir.c_str(), strerror(errno));
+    RealFileReader file_reader;
+    ManifestParser parser(&ninja.state_, &file_reader);
+    string err;
+    if (!parser.Load(options.input_file, &err)) {
+      Error("%s", err.c_str());
       return 1;
     }
-  }
 
-  BuildLog build_log;
-  if (!OpenBuildLog(&build_log, build_dir, &globals, &disk_interface))
-    return 1;
+    if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD)
+      return (ninja.*options.tool->func)(argc, argv);
 
-  DepsLog deps_log;
-  if (!OpenDepsLog(&deps_log, build_dir, &globals, &disk_interface))
-    return 1;
+    if (!ninja.EnsureBuildDirExists())
+      return 1;
 
-  if (!rebuilt_manifest) { // Don't get caught in an infinite loop by a rebuild
-                           // target that is never up to date.
-    Builder manifest_builder(globals.state, config, &build_log, &deps_log,
-                             &disk_interface);
-    if (RebuildManifest(&manifest_builder, options.input_file, &err)) {
-      rebuilt_manifest = true;
-      globals.ResetState();
-      goto reload;
-    } else if (!err.empty()) {
-      Error("rebuilding '%s': %s", options.input_file, err.c_str());
+    if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog())
       return 1;
+
+    if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS)
+      return (ninja.*options.tool->func)(argc, argv);
+
+    // The first time through, attempt to rebuild the manifest before
+    // building anything else.
+    if (cycle == 0) {
+      if (ninja.RebuildManifest(options.input_file, &err)) {
+        // Start the build over with the new manifest.
+        continue;
+      } else if (!err.empty()) {
+        Error("rebuilding '%s': %s", options.input_file, err.c_str());
+        return 1;
+      }
     }
+
+    int result = ninja.RunBuild(argc, argv);
+    if (g_metrics)
+      ninja.DumpMetrics();
+    return result;
   }
 
-  Builder builder(globals.state, config, &build_log, &deps_log,
-                  &disk_interface);
-  int result = RunBuild(&builder, argc, argv);
-  if (g_metrics)
-    DumpMetrics(&globals);
-  return result;
+  return 1;  // Shouldn't be reached.
 }
 
 }  // anonymous namespace
@@ -973,7 +1017,7 @@ int main(int argc, char** argv) {
   __try {
     // Running inside __try ... __except suppresses any Windows error
     // dialogs for errors such as bad_alloc.
-    return NinjaMain(argc, argv);
+    return real_main(argc, argv);
   }
   __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
     // Common error situations return exitCode=1. 2 was chosen to
@@ -981,6 +1025,6 @@ int main(int argc, char** argv) {
     return 2;
   }
 #else
-  return NinjaMain(argc, argv);
+  return real_main(argc, argv);
 #endif
 }