Merge pull request #1294 from bradking/plan-track-scheduling

Track in Plan whether wanted edges have been scheduled
diff --git a/HACKING.md b/HACKING.md
index e7c91ef..9198bdf 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -13,7 +13,7 @@
 Ninja is built using itself.  To bootstrap the first binary, run the
 configure script as `./configure.py --bootstrap`.  This first compiles
 all non-test source files together, then re-builds Ninja using itself.
-You should end up with a `ninja` binary (or `ninja.exe`) in the source root.
+You should end up with a `ninja` binary (or `ninja.exe`) in the project root.
 
 #### Windows
 
diff --git a/RELEASING b/RELEASING
index 5f51b73..da4dbdd 100644
--- a/RELEASING
+++ b/RELEASING
@@ -1,19 +1,20 @@
 Notes to myself on all the steps to make for a Ninja release.
 
 Push new release branch:
-1. Consider sending a heads-up to the ninja-build mailing list first
-2. Make sure branches 'master' and 'release' are synced up locally
-3. update src/version.cc with new version (with ".git"), then
+1. Run afl-fuzz for a day or so (see HACKING.md) and run ninja_test
+2. Consider sending a heads-up to the ninja-build mailing list first
+3. Make sure branches 'master' and 'release' are synced up locally
+4. Update src/version.cc with new version (with ".git"), then
        git commit -am 'mark this 1.5.0.git'
-4. git checkout release; git merge master
-5. fix version number in src/version.cc (it will likely conflict in the above)
-6. fix version in doc/manual.asciidoc (exists only on release branch)
-7. commit, tag, push (don't forget to push --tags)
+5. git checkout release; git merge master
+6. Fix version number in src/version.cc (it will likely conflict in the above)
+7. Fix version in doc/manual.asciidoc (exists only on release branch)
+8. commit, tag, push (don't forget to push --tags)
        git commit -am v1.5.0; git push origin release
        git tag v1.5.0; git push --tags
        # Push the 1.5.0.git change on master too:
        git checkout master; git push origin master
-8. construct release notes from prior notes
+9. Construct release notes from prior notes
    credits: git shortlog -s --no-merges REV..
 
 Release on github:
diff --git a/misc/ninja.vim b/misc/ninja.vim
index 190d9ce..6912d0d 100644
--- a/misc/ninja.vim
+++ b/misc/ninja.vim
@@ -21,7 +21,10 @@
 
 syn case match
 
-syn match ninjaComment /#.*/  contains=@Spell
+" Comments are only matched when the # is at the beginning of the line (with
+" optional whitespace), as long as the prior line didn't end with a $
+" continuation.
+syn match ninjaComment /\(\$\n\)\@<!\_^\s*#.*$/  contains=@Spell
 
 " Toplevel statements are the ones listed here and
 " toplevel variable assignments (ident '=' value).
@@ -38,12 +41,13 @@
 " limited set of magic variables, 'build' allows general
 " let assignments.
 " manifest_parser.cc, ParseRule()
-syn region ninjaRule start="^rule" end="^\ze\S" contains=ALL transparent
-syn keyword ninjaRuleCommand contained command deps depfile description generator
+syn region ninjaRule start="^rule" end="^\ze\S" contains=TOP transparent
+syn keyword ninjaRuleCommand contained containedin=ninjaRule command
+                                     \ deps depfile description generator
                                      \ pool restat rspfile rspfile_content
 
-syn region ninjaPool start="^pool" end="^\ze\S" contains=ALL transparent
-syn keyword ninjaPoolCommand contained depth
+syn region ninjaPool start="^pool" end="^\ze\S" contains=TOP transparent
+syn keyword ninjaPoolCommand contained containedin=ninjaPool  depth
 
 " Strings are parsed as follows:
 " lexer.in.cc, ReadEvalString()
diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py
index 5c52ea2..051bac1 100644
--- a/misc/ninja_syntax.py
+++ b/misc/ninja_syntax.py
@@ -60,7 +60,7 @@
             self.variable('deps', deps, indent=1)
 
     def build(self, outputs, rule, inputs=None, implicit=None, order_only=None,
-              variables=None, implicit_outputs=None):
+              variables=None, implicit_outputs=None, pool=None):
         outputs = as_list(outputs)
         out_outputs = [escape_path(x) for x in outputs]
         all_inputs = [escape_path(x) for x in as_list(inputs)]
@@ -81,6 +81,8 @@
 
         self._line('build %s: %s' % (' '.join(out_outputs),
                                      ' '.join([rule] + all_inputs)))
+        if pool is not None:
+            self._line('  pool = %s' % pool)
 
         if variables:
             if isinstance(variables, dict):
diff --git a/src/build.cc b/src/build.cc
index b888288..0eda16b 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -443,7 +443,12 @@
     vector<Node*>::iterator
         begin = (*oe)->inputs_.begin(),
         end = (*oe)->inputs_.end() - (*oe)->order_only_deps_;
-    if (find_if(begin, end, mem_fun(&Node::dirty)) == end) {
+#if __cplusplus < 201703L
+#define MEM_FN mem_fun
+#else
+#define MEM_FN mem_fn  // mem_fun was removed in C++17.
+#endif
+    if (find_if(begin, end, MEM_FN(&Node::dirty)) == end) {
       // Recompute most_recent_input.
       Node* most_recent_input = NULL;
       for (vector<Node*>::iterator i = begin; i != end; ++i) {
diff --git a/src/build.h b/src/build.h
index d7a9a79..9b90e8a 100644
--- a/src/build.h
+++ b/src/build.h
@@ -190,7 +190,11 @@
   State* state_;
   const BuildConfig& config_;
   Plan plan_;
+#if __cplusplus < 201703L
   auto_ptr<CommandRunner> command_runner_;
+#else
+  unique_ptr<CommandRunner> command_runner_;  // auto_ptr was removed in C++17.
+#endif
   BuildStatus* status_;
 
  private:
diff --git a/src/build_log.cc b/src/build_log.cc
index 333915a..c75be95 100644
--- a/src/build_log.cc
+++ b/src/build_log.cc
@@ -167,6 +167,9 @@
     if (log_file_) {
       if (!WriteEntry(log_file_, *log_entry))
         return false;
+      if (fflush(log_file_) != 0) {
+          return false;
+      }
     }
   }
   return true;
@@ -290,7 +293,7 @@
     if (!end)
       continue;
     *end = 0;
-    restat_mtime = atol(start);
+    restat_mtime = strtoll(start, NULL, 10);
     start = end + 1;
 
     end = (char*)memchr(start, kFieldSeparator, line_end - start);
@@ -353,7 +356,7 @@
 }
 
 bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) {
-  return fprintf(f, "%d\t%d\t%d\t%s\t%" PRIx64 "\n",
+  return fprintf(f, "%d\t%d\t%" PRId64 "\t%s\t%" PRIx64 "\n",
           entry.start_time, entry.end_time, entry.mtime,
           entry.output.c_str(), entry.command_hash) > 0;
 }
diff --git a/src/clean.cc b/src/clean.cc
index 1d6ba9e..4f31a03 100644
--- a/src/clean.cc
+++ b/src/clean.cc
@@ -180,15 +180,22 @@
   Reset();
   PrintHeader();
   for (int i = 0; i < target_count; ++i) {
-    const char* target_name = targets[i];
-    Node* target = state_->LookupNode(target_name);
-    if (target) {
-      if (IsVerbose())
-        printf("Target %s\n", target_name);
-      DoCleanTarget(target);
-    } else {
-      Error("unknown target '%s'", target_name);
+    string target_name = targets[i];
+    uint64_t slash_bits;
+    string err;
+    if (!CanonicalizePath(&target_name, &slash_bits, &err)) {
+      Error("failed to canonicalize '%s': %s", target_name.c_str(), err.c_str());
       status_ = 1;
+    } else {
+      Node* target = state_->LookupNode(target_name);
+      if (target) {
+        if (IsVerbose())
+          printf("Target %s\n", target_name.c_str());
+        DoCleanTarget(target);
+      } else {
+        Error("unknown target '%s'", target_name.c_str());
+        status_ = 1;
+      }
     }
   }
   PrintFooter();
diff --git a/src/deps_log.cc b/src/deps_log.cc
index 89c6023..eb81a37 100644
--- a/src/deps_log.cc
+++ b/src/deps_log.cc
@@ -30,7 +30,7 @@
 // The version is stored as 4 bytes after the signature and also serves as a
 // byte order mark. Signature and version combined are 16 bytes long.
 const char kFileSignature[] = "# ninjadeps\n";
-const int kCurrentVersion = 3;
+const int kCurrentVersion = 4;
 
 // Record size is currently limited to less than the full 32 bit, due to
 // internal buffers having to have this size.
@@ -124,7 +124,7 @@
     return true;
 
   // Update on-disk representation.
-  unsigned size = 4 * (1 + 1 + node_count);
+  unsigned size = 4 * (1 + 2 + node_count);
   if (size > kMaxRecordSize) {
     errno = ERANGE;
     return false;
@@ -135,8 +135,11 @@
   int id = node->id();
   if (fwrite(&id, 4, 1, file_) < 1)
     return false;
-  int timestamp = mtime;
-  if (fwrite(&timestamp, 4, 1, file_) < 1)
+  uint32_t mtime_part = static_cast<uint32_t>(mtime & 0xffffffff);
+  if (fwrite(&mtime_part, 4, 1, file_) < 1)
+    return false;
+  mtime_part = static_cast<uint32_t>((mtime >> 32) & 0xffffffff);
+  if (fwrite(&mtime_part, 4, 1, file_) < 1)
     return false;
   for (int i = 0; i < node_count; ++i) {
     id = nodes[i]->id();
@@ -209,7 +212,7 @@
     bool is_deps = (size >> 31) != 0;
     size = size & 0x7FFFFFFF;
 
-    if (fread(buf, size, 1, f) < 1 || size > kMaxRecordSize) {
+    if (size > kMaxRecordSize || fread(buf, size, 1, f) < 1) {
       read_failed = true;
       break;
     }
@@ -218,9 +221,11 @@
       assert(size % 4 == 0);
       int* deps_data = reinterpret_cast<int*>(buf);
       int out_id = deps_data[0];
-      int mtime = deps_data[1];
-      deps_data += 2;
-      int deps_count = (size / 4) - 2;
+      TimeStamp mtime;
+      mtime = (TimeStamp)(((uint64_t)(unsigned int)deps_data[2] << 32) |
+                          (uint64_t)(unsigned int)deps_data[1]);
+      deps_data += 3;
+      int deps_count = (size / 4) - 3;
 
       Deps* deps = new Deps(mtime, deps_count);
       for (int i = 0; i < deps_count; ++i) {
diff --git a/src/deps_log.h b/src/deps_log.h
index cec0257..3812a28 100644
--- a/src/deps_log.h
+++ b/src/deps_log.h
@@ -57,7 +57,9 @@
 ///      one's complement of the expected index of the record (to detect
 ///      concurrent writes of multiple ninja processes to the log).
 ///    dependency records are an array of 4-byte integers
-///      [output path id, output path mtime, input path id, input path id...]
+///      [output path id,
+///       output path mtime (lower 4 bytes), output path mtime (upper 4 bytes),
+///       input path id, input path id...]
 ///      (The mtime is compared against the on-disk output path mtime
 ///      to verify the stored data is up-to-date.)
 /// If two records reference the same output the latter one in the file
@@ -75,10 +77,10 @@
 
   // Reading (startup-time) interface.
   struct Deps {
-    Deps(int mtime, int node_count)
+    Deps(int64_t mtime, int node_count)
         : mtime(mtime), node_count(node_count), nodes(new Node*[node_count]) {}
     ~Deps() { delete [] nodes; }
-    int mtime;
+    TimeStamp mtime;
     int node_count;
     Node** nodes;
   };
diff --git a/src/disk_interface.cc b/src/disk_interface.cc
index 28530b1..f0f6346 100644
--- a/src/disk_interface.cc
+++ b/src/disk_interface.cc
@@ -61,12 +61,11 @@
 TimeStamp TimeStampFromFileTime(const FILETIME& filetime) {
   // FILETIME is in 100-nanosecond increments since the Windows epoch.
   // We don't much care about epoch correctness but we do want the
-  // resulting value to fit in an integer.
+  // resulting value to fit in a 64-bit integer.
   uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) |
     ((uint64_t)filetime.dwLowDateTime);
-  mtime /= 1000000000LL / 100; // 100ns -> s.
-  mtime -= 12622770400LL;  // 1600 epoch -> 2000 epoch (subtract 400 years).
-  return (TimeStamp)mtime;
+  // 1600 epoch -> 2000 epoch (subtract 400 years).
+  return (TimeStamp)mtime - 12622770400LL * (1000000000LL / 100);
 }
 
 TimeStamp StatSingleFile(const string& path, string* err) {
@@ -106,13 +105,19 @@
 
   if (find_handle == INVALID_HANDLE_VALUE) {
     DWORD win_err = GetLastError();
-    if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)
+    if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND ||
+        win_err == ERROR_DIRECTORY)  // File and not a directory
       return true;
     *err = "FindFirstFileExA(" + dir + "): " + GetLastErrorString();
     return false;
   }
   do {
     string lowername = ffd.cFileName;
+    if (lowername == "..") {
+      // Seems to just copy the timestamp for ".." from ".", which is wrong.
+      // This is the case at least on NTFS under Windows 7.
+      continue;
+    }
     transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
     stamps->insert(make_pair(lowername,
                              TimeStampFromFileTime(ffd.ftLastWriteTime)));
@@ -165,6 +170,11 @@
 
   string dir = DirName(path);
   string base(path.substr(dir.size() ? dir.size() + 1 : 0));
+  if (base == "..") {
+    // StatAllFilesInDir does not report any information for base = "..".
+    base = ".";
+    dir = path;
+  }
 
   transform(dir.begin(), dir.end(), dir.begin(), ::tolower);
   transform(base.begin(), base.end(), base.begin(), ::tolower);
@@ -192,7 +202,20 @@
   // that it doesn't exist.
   if (st.st_mtime == 0)
     return 1;
-  return st.st_mtime;
+#if defined(__APPLE__) && !defined(_POSIX_C_SOURCE)
+  return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL +
+          st.st_mtimespec.tv_nsec);
+#elif (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \
+       defined(__BIONIC__) || (defined (__SVR4) && defined (__sun)))
+  // For glibc, see "Timestamp files" in the Notes of http://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html
+  // newlib, uClibc and musl follow the kernel (or Cygwin) headers and define the right macro values above.
+  // For bsd, see https://github.com/freebsd/freebsd/blob/master/sys/sys/stat.h and similar
+  // For bionic, C and POSIX API is always enabled.
+  // For solaris, see https://docs.oracle.com/cd/E88353_01/html/E37841/stat-2.html.
+  return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec;
+#else
+  return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec;
+#endif
 #endif
 }
 
diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc
index d7fb8f8..5f7e468 100644
--- a/src/disk_interface_test.cc
+++ b/src/disk_interface_test.cc
@@ -63,6 +63,27 @@
   EXPECT_EQ("", err);
 }
 
+#ifdef _WIN32
+TEST_F(DiskInterfaceTest, StatMissingFileWithCache) {
+  string err;
+  disk_.AllowStatCache(true);
+
+  EXPECT_EQ(0, disk_.Stat("nosuchfile", &err));
+  EXPECT_EQ("", err);
+
+  // On Windows, the errno for a file in a nonexistent directory
+  // is different.
+  EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err));
+  EXPECT_EQ("", err);
+
+  // On POSIX systems, the errno is different if a component of the
+  // path prefix is not a directory.
+  ASSERT_TRUE(Touch("notadir"));
+  EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err));
+  EXPECT_EQ("", err);
+}
+#endif
+
 TEST_F(DiskInterfaceTest, StatBadPath) {
   string err;
 #ifdef _WIN32
@@ -87,6 +108,8 @@
   string err;
   ASSERT_TRUE(disk_.MakeDir("subdir"));
   ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
+  EXPECT_GT(disk_.Stat("..", &err), 1);
+  EXPECT_EQ("", err);
   EXPECT_GT(disk_.Stat(".", &err), 1);
   EXPECT_EQ("", err);
   EXPECT_GT(disk_.Stat("subdir", &err), 1);
@@ -105,7 +128,6 @@
 #ifdef _WIN32
 TEST_F(DiskInterfaceTest, StatCache) {
   string err;
-  disk_.AllowStatCache(true);
 
   ASSERT_TRUE(Touch("file1"));
   ASSERT_TRUE(Touch("fiLE2"));
@@ -115,6 +137,10 @@
   ASSERT_TRUE(Touch("subdir\\SUBFILE2"));
   ASSERT_TRUE(Touch("subdir\\SUBFILE3"));
 
+  disk_.AllowStatCache(false);
+  TimeStamp parent_stat_uncached = disk_.Stat("..", &err);
+  disk_.AllowStatCache(true);
+
   EXPECT_GT(disk_.Stat("FIle1", &err), 1);
   EXPECT_EQ("", err);
   EXPECT_GT(disk_.Stat("file1", &err), 1);
@@ -125,6 +151,8 @@
   EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1);
   EXPECT_EQ("", err);
 
+  EXPECT_GT(disk_.Stat("..", &err), 1);
+  EXPECT_EQ("", err);
   EXPECT_GT(disk_.Stat(".", &err), 1);
   EXPECT_EQ("", err);
   EXPECT_GT(disk_.Stat("subdir", &err), 1);
@@ -138,6 +166,8 @@
   EXPECT_EQ(disk_.Stat("subdir", &err),
             disk_.Stat("subdir/subsubdir/..", &err));
   EXPECT_EQ("", err);
+  EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached);
+  EXPECT_EQ("", err);
   EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err),
             disk_.Stat("subdir/subsubdir/.", &err));
   EXPECT_EQ("", err);
diff --git a/src/graph.cc b/src/graph.cc
index ce4ea77..b41c247 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -233,7 +233,7 @@
 
     if (output_mtime < most_recent_input->mtime()) {
       EXPLAIN("%soutput %s older than most recent input %s "
-              "(%d vs %d)",
+              "(%" PRId64 " vs %" PRId64 ")",
               used_restat ? "restat of " : "", output->path().c_str(),
               most_recent_input->path().c_str(),
               output_mtime, most_recent_input->mtime());
@@ -257,7 +257,7 @@
         // mtime of the most recent input.  This can occur even when the mtime
         // on disk is newer if a previous run wrote to the output file but
         // exited with an error or was interrupted.
-        EXPLAIN("recorded mtime of %s older than most recent input %s (%d vs %d)",
+        EXPLAIN("recorded mtime of %s older than most recent input %s (%" PRId64 " vs %" PRId64 ")",
                 output->path().c_str(), most_recent_input->path().c_str(),
                 entry->mtime, most_recent_input->mtime());
         return true;
@@ -441,7 +441,7 @@
 }
 
 void Node::Dump(const char* prefix) const {
-  printf("%s <%s 0x%p> mtime: %d%s, (:%s), ",
+  printf("%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ",
          prefix, path().c_str(), this,
          mtime(), mtime() ? "" : " (:missing)",
          dirty() ? " dirty" : " clean");
@@ -547,7 +547,7 @@
 
   // Deps are invalid if the output is newer than the deps.
   if (output->mtime() > deps->mtime) {
-    EXPLAIN("stored deps info out of date for '%s' (%d vs %d)",
+    EXPLAIN("stored deps info out of date for '%s' (%" PRId64 " vs %" PRId64 ")",
             output->path().c_str(), deps->mtime, output->mtime());
     return false;
   }
diff --git a/src/ninja.cc b/src/ninja.cc
index ed004ac..245cac1 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -207,15 +207,15 @@
 "  -f FILE  specify input build file [default=build.ninja]\n"
 "\n"
 "  -j N     run N jobs in parallel [default=%d, derived from CPUs available]\n"
-"  -k N     keep going until N jobs fail [default=1]\n"
+"  -k N     keep going until N jobs fail (0 means infinity) [default=1]\n"
 "  -l N     do not start new jobs if the load average is greater than N\n"
 "  -n       dry run (don't run commands but act like they succeeded)\n"
 "  -v       show all command lines while building\n"
 "\n"
-"  -d MODE  enable debugging (use -d list to list modes)\n"
-"  -t TOOL  run a subtool (use -t list to list subtools)\n"
+"  -d MODE  enable debugging (use '-d list' to list modes)\n"
+"  -t TOOL  run a subtool (use '-t list' to list subtools)\n"
 "    terminates toplevel options; further flags are passed to the tool\n"
-"  -w FLAG  adjust warnings (use -w list to list warnings)\n",
+"  -w FLAG  adjust warnings (use '-w list' to list warnings)\n",
           kNinjaVersion, config.parallelism);
 }
 
@@ -494,7 +494,7 @@
     TimeStamp mtime = disk_interface.Stat((*it)->path(), &err);
     if (mtime == -1)
       Error("%s", err.c_str());  // Log and ignore Stat() errors;
-    printf("%s: #deps %d, deps mtime %d (%s)\n",
+    printf("%s: #deps %d, deps mtime %" PRId64 " (%s)\n",
            (*it)->path().c_str(), deps->node_count, deps->mtime,
            (!mtime || mtime > deps->mtime ? "STALE":"VALID"));
     for (int i = 0; i < deps->node_count; ++i)
@@ -691,6 +691,8 @@
         EncodeJSONString((*e)->EvaluateCommand().c_str());
         printf("\",\n    \"file\": \"");
         EncodeJSONString((*e)->inputs_[0]->path().c_str());
+        printf("\",\n    \"output\": \"");
+        EncodeJSONString((*e)->outputs_[0]->path().c_str());
         printf("\"\n  }");
 
         first = false;
@@ -1118,7 +1120,9 @@
   return -1;
 }
 
-int real_main(int argc, char** argv) {
+NORETURN void real_main(int argc, char** argv) {
+  // Use exit() instead of return in this function to avoid potentially
+  // expensive cleanup when destructing NinjaMain.
   BuildConfig config;
   Options options = {};
   options.input_file = "build.ninja";
@@ -1128,7 +1132,7 @@
 
   int exit_code = ReadFlags(&argc, &argv, &options, &config);
   if (exit_code >= 0)
-    return exit_code;
+    exit(exit_code);
 
   if (options.working_dir) {
     // The formatting of this string, complete with funny quotes, is
@@ -1147,7 +1151,7 @@
     // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed
     // by other tools.
     NinjaMain ninja(ninja_command, config);
-    return (ninja.*options.tool->func)(&options, argc, argv);
+    exit((ninja.*options.tool->func)(&options, argc, argv));
   }
 
   // Limit number of rebuilds, to prevent infinite loops.
@@ -1166,43 +1170,43 @@
     string err;
     if (!parser.Load(options.input_file, &err)) {
       Error("%s", err.c_str());
-      return 1;
+      exit(1);
     }
 
     if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD)
-      return (ninja.*options.tool->func)(&options, argc, argv);
+      exit((ninja.*options.tool->func)(&options, argc, argv));
 
     if (!ninja.EnsureBuildDirExists())
-      return 1;
+      exit(1);
 
     if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog())
-      return 1;
+      exit(1);
 
     if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS)
-      return (ninja.*options.tool->func)(&options, argc, argv);
+      exit((ninja.*options.tool->func)(&options, argc, argv));
 
     // Attempt to rebuild the manifest before building anything else
     if (ninja.RebuildManifest(options.input_file, &err)) {
       // In dry_run mode the regeneration will succeed without changing the
       // manifest forever. Better to return immediately.
       if (config.dry_run)
-        return 0;
+        exit(0);
       // 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;
+      exit(1);
     }
 
     int result = ninja.RunBuild(argc, argv);
     if (g_metrics)
       ninja.DumpMetrics();
-    return result;
+    exit(result);
   }
 
   Error("manifest '%s' still dirty after %d tries\n",
       options.input_file, kCycleLimit);
-  return 1;
+  exit(1);
 }
 
 }  // anonymous namespace
@@ -1215,7 +1219,7 @@
   __try {
     // Running inside __try ... __except suppresses any Windows error
     // dialogs for errors such as bad_alloc.
-    return real_main(argc, argv);
+    real_main(argc, argv);
   }
   __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
     // Common error situations return exitCode=1. 2 was chosen to
@@ -1223,6 +1227,6 @@
     return 2;
   }
 #else
-  return real_main(argc, argv);
+  real_main(argc, argv);
 #endif
 }
diff --git a/src/timestamp.h b/src/timestamp.h
index cee7ba8..6a7ccd0 100644
--- a/src/timestamp.h
+++ b/src/timestamp.h
@@ -15,10 +15,19 @@
 #ifndef NINJA_TIMESTAMP_H_
 #define NINJA_TIMESTAMP_H_
 
+#ifdef _WIN32
+#include "win32port.h"
+#else
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+#include <inttypes.h>
+#endif
+
 // When considering file modification times we only care to compare
 // them against one another -- we never convert them to an absolute
-// real time.  On POSIX we use time_t (seconds since epoch) and on
-// Windows we use a different value.  Both fit in an int.
-typedef int TimeStamp;
+// real time.  On POSIX we use timespec (seconds&nanoseconds since epoch)
+// and on Windows we use a different value.  Both fit in an int64.
+typedef int64_t TimeStamp;
 
 #endif  // NINJA_TIMESTAMP_H_
diff --git a/src/win32port.h b/src/win32port.h
index ce3c949..e542536 100644
--- a/src/win32port.h
+++ b/src/win32port.h
@@ -15,6 +15,13 @@
 #ifndef NINJA_WIN32PORT_H_
 #define NINJA_WIN32PORT_H_
 
+#if defined(__MINGW32__) || defined(__MINGW64__)
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+#include <inttypes.h>
+#endif
+
 typedef signed short int16_t;
 typedef unsigned short uint16_t;
 /// A 64-bit integer type
@@ -23,6 +30,7 @@
 
 // printf format specifier for uint64_t, from C99.
 #ifndef PRIu64
+#define PRId64 "I64d"
 #define PRIu64 "I64u"
 #define PRIx64 "I64x"
 #endif