v1.3.4
diff --git a/HACKING.md b/HACKING.md
index 11edf74..8e1696a 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -62,7 +62,7 @@
 * On newer Ubuntus it's only distributed as source
 
         apt-get install libgtest-dev
-        ./configure --with-gtest=/usr/src/gtest
+        ./configure.py --with-gtest=/usr/src/gtest
 
 * Otherwise you need to download it, unpack it, and pass
   `--with-gtest` to `configure.py`.  Get it from [its downloads
diff --git a/RELEASING b/RELEASING
index faa04a2..1110f0b 100644
--- a/RELEASING
+++ b/RELEASING
@@ -8,3 +8,5 @@
 6. commit, tag, push (don't forget to push --tags)
 7. construct release notes from prior notes
    credits: git shortlog -s --no-merges REV..
+8. update home page mention of latest version.
+
diff --git a/bootstrap.py b/bootstrap.py
index cff10ba..5682bf1 100755
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -36,6 +36,8 @@
 parser.add_option('--platform',
                   help='target platform (' + '/'.join(platform_helper.platforms()) + ')',
                   choices=platform_helper.platforms())
+parser.add_option('--force-pselect', action='store_true',
+                  help="ppoll() is used by default on Linux and OpenBSD, but older versions might need to use pselect instead",)
 (options, conf_args) = parser.parse_args()
 
 
@@ -107,6 +109,10 @@
         cflags.append('-D_WIN32_WINNT=0x0501')
     if options.x64:
         cflags.append('-m64')
+if (platform.is_linux() or platform.is_openbsd()) and not options.force_pselect:
+    cflags.append('-DUSE_PPOLL')
+if options.force_pselect:
+    conf_args.append("--force-pselect")
 args.extend(cflags)
 args.extend(ldflags)
 binary = 'ninja.bootstrap'
diff --git a/configure.py b/configure.py
index 7c90e66..22eb1e5 100755
--- a/configure.py
+++ b/configure.py
@@ -47,6 +47,8 @@
 parser.add_option('--with-python', metavar='EXE',
                   help='use EXE as the Python interpreter',
                   default=os.path.basename(sys.executable))
+parser.add_option('--force-pselect', action='store_true',
+                  help="ppoll() is used by default on Linux and OpenBSD, but older versions might need to use pselect instead",)
 (options, args) = parser.parse_args()
 if args:
     print('ERROR: extra unparsed command-line arguments:', args)
@@ -163,6 +165,9 @@
         cflags.append('-fno-omit-frame-pointer')
         libs.extend(['-Wl,--no-as-needed', '-lprofiler'])
 
+if (platform.is_linux() or platform.is_openbsd()) and not options.force_pselect:
+    cflags.append('-DUSE_PPOLL')
+
 def shell_escape(str):
     """Escape str such that it's interpreted as a single argument by
     the shell."""
diff --git a/platform_helper.py b/platform_helper.py
index 97827c3..5097f49 100644
--- a/platform_helper.py
+++ b/platform_helper.py
@@ -19,7 +19,7 @@
 
 def platforms():
     return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5',
-            'mingw', 'msvc']
+            'mingw', 'msvc', 'gnukfreebsd8']
 
 class Platform( object ):
     def __init__( self, platform):
@@ -31,6 +31,8 @@
             self._platform = 'linux'
         elif self._platform.startswith('freebsd'):
             self._platform = 'freebsd'
+        elif self._platform.startswith('gnukfreebsd8'):
+            self._platform = 'freebsd'
         elif self._platform.startswith('openbsd'):
             self._platform = 'openbsd'
         elif self._platform.startswith('solaris'):
diff --git a/src/browse.py b/src/browse.py
index 652bac2..9e59bd8 100755
--- a/src/browse.py
+++ b/src/browse.py
@@ -26,6 +26,8 @@
     import http.server as httpserver
 except ImportError:
     import BaseHTTPServer as httpserver
+import os
+import socket
 import subprocess
 import sys
 import webbrowser
@@ -183,8 +185,10 @@
 port = 8000
 httpd = httpserver.HTTPServer(('',port), RequestHandler)
 try:
-    print('Web server running on port %d, ctl-C to abort...' % port)
-    webbrowser.open_new('http://localhost:%s' % port)
+    hostname = socket.gethostname()
+    print('Web server running on %s:%d, ctl-C to abort...' % (hostname,port) )
+    print('Web server pid %d' % os.getpid(), file=sys.stderr )
+    webbrowser.open_new('http://%s:%s' % (hostname, port) )
     httpd.serve_forever()
 except KeyboardInterrupt:
     print()
diff --git a/src/build_test.cc b/src/build_test.cc
index 90c328a..ed9ade3 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -1602,3 +1602,67 @@
 
   builder.command_runner_.release();
 }
+
+/// Check that a restat rule generating a header cancels compilations correctly.
+TEST_F(BuildWithDepsLogTest, RestatDepfileDependency) {
+  string err;
+  // Note: in1 was created by the superclass SetUp().
+  const char* manifest =
+      "rule true\n"
+      "  command = true\n"  // Would be "write if out-of-date" in reality.
+      "  restat = 1\n"
+      "build header.h: true header.in\n"
+      "build out: cat in1\n"
+      "  deps = gcc\n"
+      "  depfile = in1.d\n";
+  {
+    State state;
+    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));
+    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));
+
+    // Run the build once, everything should be ok.
+    DepsLog deps_log;
+    ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err));
+    ASSERT_EQ("", err);
+
+    Builder builder(&state, config_, NULL, &deps_log, &fs_);
+    builder.command_runner_.reset(&command_runner_);
+    EXPECT_TRUE(builder.AddTarget("out", &err));
+    ASSERT_EQ("", err);
+    fs_.Create("in1.d", "out: header.h");
+    EXPECT_TRUE(builder.Build(&err));
+    EXPECT_EQ("", err);
+
+    deps_log.Close();
+    builder.command_runner_.release();
+  }
+
+  {
+    State state;
+    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));
+    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));
+
+    // Touch the input of the restat rule.
+    fs_.Tick();
+    fs_.Create("header.in", "");
+
+    // Run the build again.
+    DepsLog deps_log;
+    ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err));
+    ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err));
+
+    Builder builder(&state, config_, NULL, &deps_log, &fs_);
+    builder.command_runner_.reset(&command_runner_);
+    command_runner_.commands_ran_.clear();
+    EXPECT_TRUE(builder.AddTarget("out", &err));
+    ASSERT_EQ("", err);
+    EXPECT_TRUE(builder.Build(&err));
+    EXPECT_EQ("", err);
+
+    // Rule "true" should have run again, but the build of "out" should have
+    // been cancelled due to restat propagating through the depfile header.
+    EXPECT_EQ(1u, command_runner_.commands_ran_.size());
+
+    builder.command_runner_.release();
+  }
+}
diff --git a/src/graph.cc b/src/graph.cc
index b245e52..7a57753 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -418,6 +418,7 @@
       PreallocateSpace(edge, deps->node_count);
   for (int i = 0; i < deps->node_count; ++i, ++implicit_dep) {
     *implicit_dep = deps->nodes[i];
+    deps->nodes[i]->AddOutEdge(edge);
     CreatePhonyInEdge(*implicit_dep);
   }
   return true;
diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc
index 4bc8756..05ce75d 100644
--- a/src/includes_normalize-win32.cc
+++ b/src/includes_normalize-win32.cc
@@ -110,6 +110,6 @@
   }
   StringPiece partially_fixed(copy, len);
   if (!SameDrive(partially_fixed, relative_to))
-    return ToLower(partially_fixed.AsString());
-  return ToLower(Relativize(partially_fixed, relative_to));
+    return partially_fixed.AsString();
+  return Relativize(partially_fixed, relative_to);
 }
diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc
index 29e6755..1713d5d 100644
--- a/src/includes_normalize_test.cc
+++ b/src/includes_normalize_test.cc
@@ -52,11 +52,11 @@
 
 TEST(IncludesNormalize, Case) {
   EXPECT_EQ("b", IncludesNormalize::Normalize("Abc\\..\\b", NULL));
-  EXPECT_EQ("bdef", IncludesNormalize::Normalize("Abc\\..\\BdEf", NULL));
-  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\.\\b", NULL));
-  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\./b", NULL));
-  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\.\\B", NULL));
-  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("A\\./B", NULL));
+  EXPECT_EQ("BdEf", IncludesNormalize::Normalize("Abc\\..\\BdEf", NULL));
+  EXPECT_EQ("A\\b", IncludesNormalize::Normalize("A\\.\\b", NULL));
+  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("a\\./b", NULL));
+  EXPECT_EQ("A\\B", IncludesNormalize::Normalize("A\\.\\B", NULL));
+  EXPECT_EQ("A\\B", IncludesNormalize::Normalize("A\\./B", NULL));
 }
 
 TEST(IncludesNormalize, Join) {
@@ -91,12 +91,12 @@
   EXPECT_EQ("stuff.h",
       IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "p:\\vs08"));
   EXPECT_EQ("stuff.h",
-      IncludesNormalize::Normalize("P:\\vs08\\stuff.h", "p:\\vs08"));
+      IncludesNormalize::Normalize("P:\\Vs08\\stuff.h", "p:\\vs08"));
   EXPECT_EQ("p:\\vs08\\stuff.h",
-      IncludesNormalize::Normalize("P:\\vs08\\stuff.h", "c:\\vs08"));
-  EXPECT_EQ("p:\\vs08\\stuff.h",
-      IncludesNormalize::Normalize("P:\\vs08\\stuff.h", "D:\\stuff/things"));
-  EXPECT_EQ("p:\\vs08\\stuff.h",
+      IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "c:\\vs08"));
+  EXPECT_EQ("P:\\vs08\\stufF.h",
+      IncludesNormalize::Normalize("P:\\vs08\\stufF.h", "D:\\stuff/things"));
+  EXPECT_EQ("P:\\vs08\\stuff.h",
       IncludesNormalize::Normalize("P:/vs08\\stuff.h", "D:\\stuff/things"));
   // TODO: this fails; fix it.
   //EXPECT_EQ("P:\\wee\\stuff.h",
diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc
index be2a5e0..7c45029 100644
--- a/src/msvc_helper-win32.cc
+++ b/src/msvc_helper-win32.cc
@@ -14,6 +14,7 @@
 
 #include "msvc_helper.h"
 
+#include <algorithm>
 #include <stdio.h>
 #include <string.h>
 #include <windows.h>
@@ -63,14 +64,16 @@
 }
 
 // static
-bool CLParser::IsSystemInclude(const string& path) {
+bool CLParser::IsSystemInclude(string path) {
+  transform(path.begin(), path.end(), path.begin(), ::tolower);
   // TODO: this is a heuristic, perhaps there's a better way?
   return (path.find("program files") != string::npos ||
           path.find("microsoft visual studio") != string::npos);
 }
 
 // static
-bool CLParser::FilterInputFilename(const string& line) {
+bool CLParser::FilterInputFilename(string line) {
+  transform(line.begin(), line.end(), line.begin(), ::tolower);
   // TODO: other extensions, like .asm?
   return EndsWith(line, ".c") ||
       EndsWith(line, ".cc") ||
diff --git a/src/msvc_helper.h b/src/msvc_helper.h
index 32ab606..e207485 100644
--- a/src/msvc_helper.h
+++ b/src/msvc_helper.h
@@ -30,15 +30,14 @@
   static string FilterShowIncludes(const string& line);
 
   /// Return true if a mentioned include file is a system path.
-  /// Expects the path to already by normalized (including lower case).
   /// Filtering these out reduces dependency information considerably.
-  static bool IsSystemInclude(const string& path);
+  static bool IsSystemInclude(string path);
 
   /// Parse a line of cl.exe output and return true if it looks like
   /// it's printing an input filename.  This is a heuristic but it appears
   /// to be the best we can do.
   /// Exposed for testing.
-  static bool FilterInputFilename(const string& line);
+  static bool FilterInputFilename(string line);
 
   /// Parse the full output of cl, returning the output (if any) that
   /// should printed.
diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc
index 1e1cbde..02f2863 100644
--- a/src/msvc_helper_test.cc
+++ b/src/msvc_helper_test.cc
@@ -35,6 +35,7 @@
   ASSERT_TRUE(CLParser::FilterInputFilename("foobar.cc"));
   ASSERT_TRUE(CLParser::FilterInputFilename("foo bar.cc"));
   ASSERT_TRUE(CLParser::FilterInputFilename("baz.c"));
+  ASSERT_TRUE(CLParser::FilterInputFilename("FOOBAR.CC"));
 
   ASSERT_FALSE(CLParser::FilterInputFilename(
                    "src\\cl_helper.cc(166) : fatal error C1075: end "
diff --git a/src/ninja.cc b/src/ninja.cc
index b4797ed..3b381b7 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -42,34 +42,100 @@
 #include "util.h"
 #include "version.h"
 
+#ifdef _MSC_VER
 // Defined in msvc_helper_main-win32.cc.
 int MSVCHelperMain(int argc, char** argv);
 
+// Defined in minidump-win32.cc.
+void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep);
+#endif
+
 namespace {
 
-/// Global information passed into subtools.
-struct Globals {
-  Globals() : state(new State()) {}
-  ~Globals() {
-    delete state;
-  }
+struct Tool;
 
-  /// Deletes and recreates state so it is empty.
-  void ResetState() {
-    delete state;
-    state = new State();
-  }
+/// Command-line options.
+struct Options {
+  /// Build file to load.
+  const char* input_file;
 
-  /// Command line used to run Ninja.
-  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;
+  /// Directory to change into before running.
+  const char* working_dir;
+
+  /// Tool to run rather than building.
+  const Tool* tool;
 };
 
-/// The type of functions that are the entry points to tools (subcommands).
-typedef int (*ToolFunc)(Globals*, int, char**);
+/// 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_;
+
+  /// Build configuration set from flags (e.g. parallelism).
+  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);
+
+  /// 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 {
@@ -86,10 +152,13 @@
 
     /// 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.
@@ -143,20 +212,21 @@
 
 /// 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
@@ -164,7 +234,7 @@
   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;
@@ -176,7 +246,7 @@
     first_dependent = true;
   }
 
-  Node* node = state->LookupNode(path);
+  Node* node = state_.LookupNode(path);
   if (node) {
     if (first_dependent) {
       if (node->out_edges().empty()) {
@@ -199,7 +269,7 @@
     } 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() + "'?";
       }
@@ -208,15 +278,15 @@
   }
 }
 
-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);
@@ -224,10 +294,10 @@
   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;
   }
@@ -241,14 +311,15 @@
   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;
@@ -279,19 +350,19 @@
 }
 
 #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[]) {
+#if defined(_MSC_VER)
+int NinjaMain::ToolMSVC(int argc, char* argv[]) {
   // Reset getopt: push one argument onto the front of argv, reset optind.
   argc++;
   argv--;
@@ -366,7 +437,7 @@
   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];
@@ -375,17 +446,17 @@
       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, "rule", "depth", "all", NULL);
+          SpellcheckString(mode.c_str(), "rule", "depth", "all", NULL);
       if (suggestion) {
         Error("unknown target tool mode '%s', did you mean '%s'?",
               mode.c_str(), suggestion);
@@ -397,7 +468,7 @@
   }
 
   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 {
@@ -420,10 +491,10 @@
     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;
   }
@@ -435,7 +506,7 @@
   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++;
@@ -473,7 +544,7 @@
     return 1;
   }
 
-  Cleaner cleaner(globals->state, *globals->config);
+  Cleaner cleaner(&state_, config_);
   if (argc >= 1) {
     if (clean_rules)
       return cleaner.CleanRules(argc, argv);
@@ -493,25 +564,29 @@
   }
 }
 
-int ToolCompilationDatabase(Globals* globals, int argc, char* argv[]) {
+int NinjaMain::ToolCompilationDatabase(int argc, char* argv[]) {
   bool first = true;
-  char cwd[PATH_MAX];
+  vector<char> cwd;
 
-  if (!getcwd(cwd, PATH_MAX)) {
+  do {
+    cwd.resize(cwd.size() + 1024);
+    errno = 0;
+  } while (!getcwd(&cwd[0], cwd.size()) && errno == ERANGE);
+  if (errno != 0 && errno != ERANGE) {
     Error("cannot determine working directory: %s", strerror(errno));
     return 1;
   }
 
   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)
           putchar(',');
 
         printf("\n  {\n    \"directory\": \"");
-        EncodeJSONString(cwd);
+        EncodeJSONString(&cwd[0]);
         printf("\",\n    \"command\": \"");
         EncodeJSONString((*e)->EvaluateCommand().c_str());
         printf("\",\n    \"file\": \"");
@@ -527,7 +602,7 @@
   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 "
@@ -554,31 +629,31 @@
 }
 
 /// Find the function to execute for \a tool_name and return it via \a func.
-/// If there is no tool to run (e.g.: unknown tool), returns an exit code.
-int ChooseTool(const string& tool_name, const Tool** tool_out) {
+/// Returns a Tool, or NULL if Ninja should exit.
+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)
+#if defined(_MSC_VER)
     { "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 }
   };
 
@@ -588,14 +663,12 @@
       if (tool->desc)
         printf("%10s  %s\n", tool->name, tool->desc);
     }
-    return 0;
+    return NULL;
   }
 
   for (const Tool* tool = &kTools[0]; tool->name; ++tool) {
-    if (tool->name == tool_name) {
-      *tool_out = tool;
-      return 0;
-    }
+    if (tool->name == tool_name)
+      return tool;
   }
 
   vector<const char*> words;
@@ -603,17 +676,17 @@
     words.push_back(tool->name);
   const char* suggestion = SpellcheckStringV(tool_name, words);
   if (suggestion) {
-    Error("unknown tool '%s', did you mean '%s'?",
+    Fatal("unknown tool '%s', did you mean '%s'?",
           tool_name.c_str(), suggestion);
   } else {
-    Error("unknown tool '%s'", tool_name.c_str());
+    Fatal("unknown tool '%s'", tool_name.c_str());
   }
-  return 1;
+  return NULL;  // Not reached.
 }
 
 /// Enable a debugging mode.  Returns false if Ninja should exit instead
 /// of continuing.
-bool DebugEnable(const string& name, Globals* globals) {
+bool DebugEnable(const string& name) {
   if (name == "list") {
     printf("debugging modes:\n"
 "  stats    print operation counts/timing info\n"
@@ -628,7 +701,7 @@
     return true;
   } else {
     const char* suggestion =
-        SpellcheckString(name, "stats", "explain", NULL);
+        SpellcheckString(name.c_str(), "stats", "explain", NULL);
     if (suggestion) {
       Error("unknown debug setting '%s', did you mean '%s'?",
             name.c_str(), suggestion);
@@ -639,16 +712,13 @@
   }
 }
 
-/// 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;
   }
@@ -658,8 +728,8 @@
     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;
     }
@@ -670,14 +740,13 @@
 
 /// 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;
   }
@@ -687,8 +756,8 @@
     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;
     }
@@ -697,28 +766,39 @@
   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;
@@ -729,15 +809,15 @@
     }
   }
 
-  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;
   }
@@ -747,13 +827,6 @@
 
 #ifdef _MSC_VER
 
-} // anonymous namespace
-
-// Defined in minidump-win32.cc.
-void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep);
-
-namespace {
-
 /// This handler processes fatal crashes that you can't catch
 /// Test example: C++ exception in a stack-unwind-block
 /// Real-world example: ninja launched a compiler to process a tricky
@@ -775,18 +848,11 @@
 
 #endif  // _MSC_VER
 
-int NinjaMain(int argc, char** argv) {
-  BuildConfig config;
-  Globals globals;
-  globals.ninja_command = argv[0];
-  globals.config = &config;
-  const char* input_file = "build.ninja";
-  const char* working_dir = NULL;
-  string tool_name;
-
-  setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
-
-  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[] = {
@@ -796,20 +862,25 @@
   };
 
   int opt;
-  while (tool_name.empty() &&
-         (opt = getopt_long(argc, argv, "d:f:j:k:l:nt:vC:h", kLongOptions,
+  while (!options->tool &&
+         (opt = getopt_long(*argc, *argv, "d:f:j:k:l:nt:vC:h", kLongOptions,
                             NULL)) != -1) {
     switch (opt) {
       case 'd':
-        if (!DebugEnable(optarg, &globals))
+        if (!DebugEnable(optarg))
           return 1;
         break;
       case 'f':
-        input_file = optarg;
+        options->input_file = optarg;
         break;
-      case 'j':
-        config.parallelism = atoi(optarg);
+      case 'j': {
+        char* end;
+        int value = strtol(optarg, &end, 10);
+        if (*end != 0 || value <= 0)
+          Fatal("invalid -j parameter");
+        config->parallelism = value;
         break;
+      }
       case 'k': {
         char* end;
         int value = strtol(optarg, &end, 10);
@@ -819,7 +890,7 @@
         // 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': {
@@ -827,113 +898,113 @@
         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':
-        tool_name = optarg;
+        options->tool = ChooseTool(optarg);
+        if (!options->tool)
+          return 0;
         break;
       case 'v':
-        config.verbosity = BuildConfig::VERBOSE;
+        config->verbosity = BuildConfig::VERBOSE;
         break;
       case 'C':
-        working_dir = optarg;
+        options->working_dir = optarg;
         break;
       case OPT_VERSION:
         printf("%s\n", kNinjaVersion);
         return 0;
       case 'h':
       default:
-        Usage(config);
+        Usage(*config);
         return 1;
     }
   }
-  argv += optind;
-  argc -= optind;
+  *argv += optind;
+  *argc -= optind;
 
-  // If specified, select a tool as early as possible, so commands like
-  // -t list can run before we attempt to load build.ninja etc.
-  const Tool* tool = NULL;
-  if (!tool_name.empty()) {
-    int exit_code = ChooseTool(tool_name, &tool);
-    if (!tool)
-      return exit_code;
+  return -1;
+}
+
+int real_main(int argc, char** argv) {
+  BuildConfig config;
+  Options options = {};
+  options.input_file = "build.ninja";
+
+  setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+
+  int exit_code = ReadFlags(&argc, &argv, &options, &config);
+  if (exit_code >= 0)
+    return exit_code;
+
+  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 (tool && tool->when == Tool::RUN_AFTER_FLAGS)
-    return tool->func(&globals, argc, argv);
-
-  if (working_dir) {
+  if (options.working_dir) {
     // The formatting of this string, complete with funny quotes, is
     // so Emacs can properly identify that the cwd has changed for
     // 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)
-      printf("ninja: Entering directory `%s'\n", working_dir);
-    if (chdir(working_dir) < 0) {
-      Fatal("chdir to '%s' - %s", working_dir, strerror(errno));
+    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;
+  // 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);
 
-reload:
-  RealFileReader file_reader;
-  ManifestParser parser(globals.state, &file_reader);
-  string err;
-  if (!parser.Load(input_file, &err)) {
-    Error("%s", err.c_str());
-    return 1;
-  }
-
-  if (tool && tool->when == Tool::RUN_AFTER_LOAD)
-    return tool->func(&globals, argc, argv);
-
-  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 (!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, input_file, &err)) {
-      rebuilt_manifest = true;
-      globals.ResetState();
-      goto reload;
-    } else if (!err.empty()) {
-      Error("rebuilding '%s': %s", input_file, err.c_str());
+    if (!ninja.EnsureBuildDirExists())
       return 1;
+
+    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
@@ -946,7 +1017,7 @@
   __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
@@ -954,6 +1025,6 @@
     return 2;
   }
 #else
-  return NinjaMain(argc, argv);
+  return real_main(argc, argv);
 #endif
 }
diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc
index 339edfe..b396f84 100644
--- a/src/subprocess-posix.cc
+++ b/src/subprocess-posix.cc
@@ -40,12 +40,12 @@
   if (pipe(output_pipe) < 0)
     Fatal("pipe: %s", strerror(errno));
   fd_ = output_pipe[0];
-#if !defined(linux) && !defined(__OpenBSD__)
+#if !defined(USE_PPOLL)
   // On Linux and OpenBSD, we use ppoll in DoWork(); elsewhere we use pselect
   // and so must avoid overly-large FDs.
   if (fd_ >= static_cast<int>(FD_SETSIZE))
     Fatal("pipe: %s", strerror(EMFILE));
-#endif  // !linux && !__OpenBSD__
+#endif  // !USE_PPOLL
   SetCloseOnExec(fd_);
 
   pid_ = fork();
@@ -178,7 +178,7 @@
   return subprocess;
 }
 
-#if defined(linux) || defined(__OpenBSD__)
+#ifdef USE_PPOLL
 bool SubprocessSet::DoWork() {
   vector<pollfd> fds;
   nfds_t nfds = 0;
diff --git a/src/util.cc b/src/util.cc
index fa72dd2..b9c2c0d 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -234,13 +234,16 @@
   return result;
 }
 
-const char* SpellcheckString(const string& text, ...) {
+const char* SpellcheckString(const char* text, ...) {
+  // Note: This takes a const char* instead of a string& because using
+  // va_start() with a reference parameter is undefined behavior.
   va_list ap;
   va_start(ap, text);
   vector<const char*> words;
   const char* word;
   while ((word = va_arg(ap, const char*)))
     words.push_back(word);
+  va_end(ap);
   return SpellcheckStringV(text, words);
 }
 
diff --git a/src/util.h b/src/util.h
index 9740565..6788410 100644
--- a/src/util.h
+++ b/src/util.h
@@ -59,7 +59,7 @@
                               const vector<const char*>& words);
 
 /// Like SpellcheckStringV, but takes a NULL-terminated list.
-const char* SpellcheckString(const string& text, ...);
+const char* SpellcheckString(const char* text, ...);
 
 /// Removes all Ansi escape codes (http://www.termsys.demon.co.uk/vtansi.htm).
 string StripAnsiEscapeCodes(const string& in);
diff --git a/src/version.cc b/src/version.cc
index 54818a0..28280aa 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -18,7 +18,7 @@
 
 #include "util.h"
 
-const char* kNinjaVersion = "1.3.3";
+const char* kNinjaVersion = "1.3.4";
 
 void ParseVersion(const string& version, int* major, int* minor) {
   size_t end = version.find('.');