Merge pull request #1350 from korli/posix_spawn_errno

posix_spawn_* calls don't set errno, use the return value.
diff --git a/HACKING.md b/HACKING.md
index 9198bdf..5e88958 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -95,7 +95,7 @@
 Generally it's the [Google C++ coding style][], but in brief:
 
 * Function name are camelcase.
-* Member methods are camelcase, expect for trivial getters which are
+* Member methods are camelcase, except for trivial getters which are
   underscore separated.
 * Local variables are underscore separated.
 * Member variables are underscore separated and suffixed by an extra
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..c8e1a9d
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,14 @@
+version: 1.0.{build}
+image: Visual Studio 2017
+build_script:
+- cmd: >-
+    call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
+
+    python configure.py --bootstrap
+
+    ninja.bootstrap.exe all
+
+    ninja_test
+
+    python misc/ninja_syntax_test.py
+test: off
diff --git a/configure.py b/configure.py
index a443748..9e23a5a 100755
--- a/configure.py
+++ b/configure.py
@@ -256,7 +256,7 @@
 if '--bootstrap' in configure_args:
     configure_args.remove('--bootstrap')
 n.variable('configure_args', ' '.join(configure_args))
-env_keys = set(['CXX', 'AR', 'CFLAGS', 'LDFLAGS'])
+env_keys = set(['CXX', 'AR', 'CFLAGS', 'CXXFLAGS', 'LDFLAGS'])
 configure_env = dict((k, os.environ[k]) for k in os.environ if k in env_keys)
 if configure_env:
     config_str = ' '.join([k + '=' + pipes.quote(configure_env[k])
@@ -397,6 +397,10 @@
 
 if 'CFLAGS' in configure_env:
     cflags.append(configure_env['CFLAGS'])
+    ldflags.append(configure_env['CFLAGS'])
+if 'CXXFLAGS' in configure_env:
+    cflags.append(configure_env['CXXFLAGS'])
+    ldflags.append(configure_env['CXXFLAGS'])
 n.variable('cflags', ' '.join(shell_escape(flag) for flag in cflags))
 if 'LDFLAGS' in configure_env:
     ldflags.append(configure_env['LDFLAGS'])
diff --git a/misc/ninja-mode.el b/misc/ninja-mode.el
index 639e537..8b975d5 100644
--- a/misc/ninja-mode.el
+++ b/misc/ninja-mode.el
@@ -56,7 +56,7 @@
                (save-excursion
                  (goto-char (line-end-position 0))
                  (or
-                  ;; If we're continuting the previous line, it's not a
+                  ;; If we're continuing the previous line, it's not a
                   ;; comment.
                   (not (eq ?$ (char-before)))
                   ;; Except if the previous line is a comment as well, as the
diff --git a/misc/ninja.vim b/misc/ninja.vim
index 190d9ce..c1ffd50 100644
--- a/misc/ninja.vim
+++ b/misc/ninja.vim
@@ -1,8 +1,8 @@
 " ninja build file syntax.
 " Language: ninja build file as described at
 "           http://ninja-build.org/manual.html
-" Version: 1.4
-" Last Change: 2014/05/13
+" Version: 1.5
+" Last Change: 2018/04/05
 " Maintainer: Nicolas Weber <nicolasweber@gmx.de>
 " Version 1.4 of this script is in the upstream vim repository and will be
 " included in the next vim release. If you change this, please send your change
@@ -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/misc/ninja_syntax_test.py b/misc/ninja_syntax_test.py
index 07e3ed3..90ff9c6 100755
--- a/misc/ninja_syntax_test.py
+++ b/misc/ninja_syntax_test.py
@@ -46,13 +46,13 @@
                          self.out.getvalue())
 
     def test_comment_wrap(self):
-        # Filenames shoud not be wrapped
+        # Filenames should not be wrapped
         self.n.comment('Hello /usr/local/build-tools/bin')
         self.assertEqual('# Hello\n# /usr/local/build-tools/bin\n',
                          self.out.getvalue())
 
     def test_short_words_indented(self):
-        # Test that indent is taking into acount when breaking subsequent lines.
+        # Test that indent is taking into account when breaking subsequent lines.
         # The second line should not be '    to tree', as that's longer than the
         # test layout width of 8.
         self.n._line('line_one to tree')
diff --git a/src/build.cc b/src/build.cc
index 61ef0e8..c24d6a9 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -16,6 +16,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <functional>
@@ -130,6 +131,18 @@
 
   if (!edge->use_console())
     PrintStatus(edge, kEdgeFinished);
+  if (printer_.is_smart_terminal()) {
+    int oldest_start = INT_MAX;
+    Edge* oldest = NULL;
+    for (i = running_edges_.begin(); i != running_edges_.end(); i++) {
+      if (i->second < oldest_start) {
+        oldest_start = i->second;
+        oldest = i->first;
+      }
+    }
+    if (oldest)
+      PrintStatus(oldest, kEdgeRunning);
+  }
 
   // Print the command that is spewing before printing its output.
   if (!success) {
@@ -318,18 +331,18 @@
     return false;  // Don't need to do anything.
 
   // If an entry in want_ does not already exist for edge, create an entry which
-  // maps to false, indicating that we do not want to build this entry itself.
-  pair<map<Edge*, bool>::iterator, bool> want_ins =
-    want_.insert(make_pair(edge, false));
-  bool& want = want_ins.first->second;
+  // maps to kWantNothing, indicating that we do not want to build this entry itself.
+  pair<map<Edge*, Want>::iterator, bool> want_ins =
+    want_.insert(make_pair(edge, kWantNothing));
+  Want& want = want_ins.first->second;
 
   // If we do need to build edge and we haven't already marked it as wanted,
   // mark it now.
-  if (node->dirty() && !want) {
-    want = true;
+  if (node->dirty() && want == kWantNothing) {
+    want = kWantToStart;
     ++wanted_edges_;
     if (edge->AllInputsReady())
-      ScheduleWork(edge);
+      ScheduleWork(want_ins.first);
     if (!edge->is_phony())
       ++command_edges_;
   }
@@ -355,30 +368,32 @@
   return edge;
 }
 
-void Plan::ScheduleWork(Edge* edge) {
-  set<Edge*>::iterator e = ready_.lower_bound(edge);
-  if (e != ready_.end() && !ready_.key_comp()(edge, *e)) {
+void Plan::ScheduleWork(map<Edge*, Want>::iterator want_e) {
+  if (want_e->second == kWantToFinish) {
     // This edge has already been scheduled.  We can get here again if an edge
     // and one of its dependencies share an order-only input, or if a node
     // duplicates an out edge (see https://github.com/ninja-build/ninja/pull/519).
     // Avoid scheduling the work again.
     return;
   }
+  assert(want_e->second == kWantToStart);
+  want_e->second = kWantToFinish;
 
+  Edge* edge = want_e->first;
   Pool* pool = edge->pool();
   if (pool->ShouldDelayEdge()) {
     pool->DelayEdge(edge);
     pool->RetrieveReadyEdges(&ready_);
   } else {
     pool->EdgeScheduled(*edge);
-    ready_.insert(e, edge);
+    ready_.insert(edge);
   }
 }
 
 void Plan::EdgeFinished(Edge* edge, EdgeResult result) {
-  map<Edge*, bool>::iterator e = want_.find(edge);
+  map<Edge*, Want>::iterator e = want_.find(edge);
   assert(e != want_.end());
-  bool directly_wanted = e->second;
+  bool directly_wanted = e->second != kWantNothing;
 
   // See if this job frees up any delayed jobs.
   if (directly_wanted)
@@ -405,14 +420,14 @@
   // See if we we want any edges from this node.
   for (vector<Edge*>::const_iterator oe = node->out_edges().begin();
        oe != node->out_edges().end(); ++oe) {
-    map<Edge*, bool>::iterator want_e = want_.find(*oe);
+    map<Edge*, Want>::iterator want_e = want_.find(*oe);
     if (want_e == want_.end())
       continue;
 
     // See if the edge is now ready.
     if ((*oe)->AllInputsReady()) {
-      if (want_e->second) {
-        ScheduleWork(*oe);
+      if (want_e->second != kWantNothing) {
+        ScheduleWork(want_e);
       } else {
         // We do not need to build this edge, but we might need to build one of
         // its dependents.
@@ -428,8 +443,8 @@
   for (vector<Edge*>::const_iterator oe = node->out_edges().begin();
        oe != node->out_edges().end(); ++oe) {
     // Don't process edges that we don't actually want.
-    map<Edge*, bool>::iterator want_e = want_.find(*oe);
-    if (want_e == want_.end() || !want_e->second)
+    map<Edge*, Want>::iterator want_e = want_.find(*oe);
+    if (want_e == want_.end() || want_e->second == kWantNothing)
       continue;
 
     // Don't attempt to clean an edge if it failed to load deps.
@@ -441,7 +456,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) {
@@ -464,7 +484,7 @@
             return false;
         }
 
-        want_e->second = false;
+        want_e->second = kWantNothing;
         --wanted_edges_;
         if (!(*oe)->is_phony())
           --command_edges_;
@@ -476,8 +496,8 @@
 
 void Plan::Dump() {
   printf("pending: %d\n", (int)want_.size());
-  for (map<Edge*, bool>::iterator e = want_.begin(); e != want_.end(); ++e) {
-    if (e->second)
+  for (map<Edge*, Want>::iterator e = want_.begin(); e != want_.end(); ++e) {
+    if (e->second != kWantNothing)
       printf("want ");
     e->first->Dump();
   }
diff --git a/src/build.h b/src/build.h
index 43786f1..ac7f951 100644
--- a/src/build.h
+++ b/src/build.h
@@ -78,17 +78,29 @@
   bool AddSubTarget(Node* node, Node* dependent, string* err);
   void NodeFinished(Node* node);
 
+  /// Enumerate possible steps we want for an edge.
+  enum Want
+  {
+    /// We do not want to build the edge, but we might want to build one of
+    /// its dependents.
+    kWantNothing,
+    /// We want to build the edge, but have not yet scheduled it.
+    kWantToStart,
+    /// We want to build the edge, have scheduled it, and are waiting
+    /// for it to complete.
+    kWantToFinish
+  };
+
   /// Submits a ready edge as a candidate for execution.
   /// The edge may be delayed from running, for example if it's a member of a
   /// currently-full pool.
-  void ScheduleWork(Edge* edge);
+  void ScheduleWork(map<Edge*, Want>::iterator want_e);
 
   /// Keep track of which edges we want to build in this plan.  If this map does
   /// not contain an entry for an edge, we do not want to build the entry or its
-  /// dependents.  If an entry maps to false, we do not want to build it, but we
-  /// might want to build one of its dependents.  If the entry maps to true, we
-  /// want to build it.
-  map<Edge*, bool> want_;
+  /// dependents.  If it does contain an entry, the enumeration indicates what
+  /// we want for the edge.
+  map<Edge*, Want> want_;
 
   set<Edge*> ready_;
 
@@ -178,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:
@@ -206,6 +222,7 @@
 
   enum EdgeStatus {
     kEdgeStarted,
+    kEdgeRunning,
     kEdgeFinished,
   };
 
diff --git a/src/build_log.cc b/src/build_log.cc
index 648617c..2a65f9d 100644
--- a/src/build_log.cc
+++ b/src/build_log.cc
@@ -76,11 +76,17 @@
   switch (len & 7)
   {
   case 7: h ^= uint64_t(data[6]) << 48;
+          NINJA_FALLTHROUGH;
   case 6: h ^= uint64_t(data[5]) << 40;
+          NINJA_FALLTHROUGH;
   case 5: h ^= uint64_t(data[4]) << 32;
+          NINJA_FALLTHROUGH;
   case 4: h ^= uint64_t(data[3]) << 24;
+          NINJA_FALLTHROUGH;
   case 3: h ^= uint64_t(data[2]) << 16;
+          NINJA_FALLTHROUGH;
   case 2: h ^= uint64_t(data[1]) << 8;
+          NINJA_FALLTHROUGH;
   case 1: h ^= uint64_t(data[0]);
           h *= m;
   };
@@ -167,6 +173,9 @@
     if (log_file_) {
       if (!WriteEntry(log_file_, *log_entry))
         return false;
+      if (fflush(log_file_) != 0) {
+          return false;
+      }
     }
   }
   return true;
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_test.cc b/src/deps_log_test.cc
index 89f7be1..0cdeb45 100644
--- a/src/deps_log_test.cc
+++ b/src/deps_log_test.cc
@@ -143,7 +143,7 @@
     ASSERT_GT(file_size, 0);
   }
 
-  // Now reload the file, and readd the same deps.
+  // Now reload the file, and read the same deps.
   {
     State state;
     DepsLog log;
@@ -203,7 +203,7 @@
     ASSERT_GT(file_size, 0);
   }
 
-  // Now reload the file, and add slighly different deps.
+  // Now reload the file, and add slightly different deps.
   int file_size_2;
   {
     State state;
diff --git a/src/disk_interface.cc b/src/disk_interface.cc
index 4b4c4c7..504c679 100644
--- a/src/disk_interface.cc
+++ b/src/disk_interface.cc
@@ -70,7 +70,7 @@
 
 TimeStamp StatSingleFile(const string& path, string* err) {
   WIN32_FILE_ATTRIBUTE_DATA attrs;
-  if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) {
+  if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) {
     DWORD win_err = GetLastError();
     if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)
       return 0;
@@ -112,6 +112,11 @@
   }
   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)));
@@ -164,6 +169,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);
@@ -195,11 +205,12 @@
   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(__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;
diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc
index d7fb8f8..81aa63a 100644
--- a/src/disk_interface_test.cc
+++ b/src/disk_interface_test.cc
@@ -87,6 +87,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 +107,6 @@
 #ifdef _WIN32
 TEST_F(DiskInterfaceTest, StatCache) {
   string err;
-  disk_.AllowStatCache(true);
 
   ASSERT_TRUE(Touch("file1"));
   ASSERT_TRUE(Touch("fiLE2"));
@@ -115,6 +116,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 +130,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 +145,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/hash_map.h b/src/hash_map.h
index a91aeb9..55d2c9d 100644
--- a/src/hash_map.h
+++ b/src/hash_map.h
@@ -18,6 +18,7 @@
 #include <algorithm>
 #include <string.h>
 #include "string_piece.h"
+#include "util.h"
 
 // MurmurHash2, by Austin Appleby
 static inline
@@ -40,7 +41,9 @@
   }
   switch (len) {
   case 3: h ^= data[2] << 16;
+          NINJA_FALLTHROUGH;
   case 2: h ^= data[1] << 8;
+          NINJA_FALLTHROUGH;
   case 1: h ^= data[0];
     h *= m;
   };
diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc
index 459329b..795542b 100644
--- a/src/includes_normalize-win32.cc
+++ b/src/includes_normalize-win32.cc
@@ -61,8 +61,8 @@
 
   char a_absolute[_MAX_PATH];
   char b_absolute[_MAX_PATH];
-  GetFullPathName(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL);
-  GetFullPathName(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL);
+  GetFullPathNameA(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL);
+  GetFullPathNameA(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL);
   char a_drive[_MAX_DIR];
   char b_drive[_MAX_DIR];
   _splitpath(a_absolute, a_drive, NULL, NULL, NULL);
@@ -122,7 +122,7 @@
   }
 
   char result[_MAX_PATH];
-  GetFullPathName(s.AsString().c_str(), sizeof(result), result, NULL);
+  GetFullPathNameA(s.AsString().c_str(), sizeof(result), result, NULL);
   for (char* c = result; *c; ++c)
     if (*c == '\\')
       *c = '/';
diff --git a/src/lexer.cc b/src/lexer.cc
index 37b8678..3c6e70e 100644
--- a/src/lexer.cc
+++ b/src/lexer.cc
@@ -23,14 +23,14 @@
 bool Lexer::Error(const string& message, string* err) {
   // Compute line/column.
   int line = 1;
-  const char* context = input_.str_;
+  const char* line_start = input_.str_;
   for (const char* p = input_.str_; p < last_token_; ++p) {
     if (*p == '\n') {
       ++line;
-      context = p + 1;
+      line_start = p + 1;
     }
   }
-  int col = last_token_ ? (int)(last_token_ - context) : 0;
+  int col = last_token_ ? (int)(last_token_ - line_start) : 0;
 
   char buf[1024];
   snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line);
@@ -43,12 +43,12 @@
     int len;
     bool truncated = true;
     for (len = 0; len < kTruncateColumn; ++len) {
-      if (context[len] == 0 || context[len] == '\n') {
+      if (line_start[len] == 0 || line_start[len] == '\n') {
         truncated = false;
         break;
       }
     }
-    *err += string(context, len);
+    *err += string(line_start, len);
     if (truncated)
       *err += "...";
     *err += "\n";
@@ -537,8 +537,9 @@
 
 bool Lexer::ReadIdent(string* out) {
   const char* p = ofs_;
+  const char* start;
   for (;;) {
-    const char* start = p;
+    start = p;
     
 {
 	unsigned char yych;
@@ -604,7 +605,10 @@
     }
 yy97:
 	++p;
-	{ return false; }
+	{
+      last_token_ = start;
+      return false;
+    }
 yy99:
 	++p;
 	yych = *p;
@@ -616,6 +620,7 @@
 }
 
   }
+  last_token_ = start;
   ofs_ = p;
   EatWhitespace();
   return true;
diff --git a/src/lexer.in.cc b/src/lexer.in.cc
index f861239..c1fb822 100644
--- a/src/lexer.in.cc
+++ b/src/lexer.in.cc
@@ -22,14 +22,14 @@
 bool Lexer::Error(const string& message, string* err) {
   // Compute line/column.
   int line = 1;
-  const char* context = input_.str_;
+  const char* line_start = input_.str_;
   for (const char* p = input_.str_; p < last_token_; ++p) {
     if (*p == '\n') {
       ++line;
-      context = p + 1;
+      line_start = p + 1;
     }
   }
-  int col = last_token_ ? (int)(last_token_ - context) : 0;
+  int col = last_token_ ? (int)(last_token_ - line_start) : 0;
 
   char buf[1024];
   snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line);
@@ -42,12 +42,12 @@
     int len;
     bool truncated = true;
     for (len = 0; len < kTruncateColumn; ++len) {
-      if (context[len] == 0 || context[len] == '\n') {
+      if (line_start[len] == 0 || line_start[len] == '\n') {
         truncated = false;
         break;
       }
     }
-    *err += string(context, len);
+    *err += string(line_start, len);
     if (truncated)
       *err += "...";
     *err += "\n";
@@ -182,16 +182,21 @@
 
 bool Lexer::ReadIdent(string* out) {
   const char* p = ofs_;
+  const char* start;
   for (;;) {
-    const char* start = p;
+    start = p;
     /*!re2c
     varname {
       out->assign(start, p - start);
       break;
     }
-    [^] { return false; }
+    [^] {
+      last_token_ = start;
+      return false;
+    }
     */
   }
+  last_token_ = start;
   ofs_ = p;
   EatWhitespace();
   return true;
diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index 39ed810..c91d8d1 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -523,7 +523,7 @@
     EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err));
     EXPECT_EQ("input:1: unknown build rule 'y'\n"
               "build x: y z\n"
-              "       ^ near here"
+              "         ^ near here"
               , err);
   }
 
@@ -534,7 +534,7 @@
     EXPECT_FALSE(parser.ParseTest("build x:: y z\n", &err));
     EXPECT_EQ("input:1: expected build command name\n"
               "build x:: y z\n"
-              "       ^ near here"
+              "        ^ near here"
               , err);
   }
 
@@ -636,7 +636,10 @@
     string err;
     EXPECT_FALSE(parser.ParseTest("rule %foo\n",
                                   &err));
-    EXPECT_EQ("input:1: expected rule name\n", err);
+    EXPECT_EQ("input:1: expected rule name\n"
+              "rule %foo\n"
+              "     ^ near here",
+              err);
   }
 
   {
@@ -672,7 +675,10 @@
     string err;
     EXPECT_FALSE(parser.ParseTest("rule cc\n  command = foo\n  && bar",
                                   &err));
-    EXPECT_EQ("input:3: expected variable name\n", err);
+    EXPECT_EQ("input:3: expected variable name\n"
+              "  && bar\n"
+              "  ^ near here",
+              err);
   }
 
   {
@@ -767,7 +773,9 @@
     ManifestParser parser(&local_state, NULL);
     string err;
     EXPECT_FALSE(parser.ParseTest("pool\n", &err));
-    EXPECT_EQ("input:1: expected pool name\n", err);
+    EXPECT_EQ("input:1: expected pool name\n"
+              "pool\n"
+              "    ^ near here", err);
   }
 
   {
diff --git a/src/minidump-win32.cc b/src/minidump-win32.cc
index 1efb085..ca93638 100644
--- a/src/minidump-win32.cc
+++ b/src/minidump-win32.cc
@@ -32,17 +32,17 @@
 /// Creates a windows minidump in temp folder.
 void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep) {
   char temp_path[MAX_PATH];
-  GetTempPath(sizeof(temp_path), temp_path);
+  GetTempPathA(sizeof(temp_path), temp_path);
   char temp_file[MAX_PATH];
   sprintf(temp_file, "%s\\ninja_crash_dump_%lu.dmp",
           temp_path, GetCurrentProcessId());
 
   // Delete any previous minidump of the same name.
-  DeleteFile(temp_file);
+  DeleteFileA(temp_file);
 
   // Load DbgHelp.dll dynamically, as library is not present on all
   // Windows versions.
-  HMODULE dbghelp = LoadLibrary("dbghelp.dll");
+  HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
   if (dbghelp == NULL) {
     Error("failed to create minidump: LoadLibrary('dbghelp.dll'): %s",
           GetLastErrorString().c_str());
diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc
index e37a26e..de6147a 100644
--- a/src/msvc_helper-win32.cc
+++ b/src/msvc_helper-win32.cc
@@ -43,10 +43,10 @@
   security_attributes.bInheritHandle = TRUE;
 
   // Must be inheritable so subprocesses can dup to children.
-  HANDLE nul = CreateFile("NUL", GENERIC_READ,
-                          FILE_SHARE_READ | FILE_SHARE_WRITE |
-                          FILE_SHARE_DELETE,
-                          &security_attributes, OPEN_EXISTING, 0, NULL);
+  HANDLE nul =
+      CreateFileA("NUL", GENERIC_READ,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  &security_attributes, OPEN_EXISTING, 0, NULL);
   if (nul == INVALID_HANDLE_VALUE)
     Fatal("couldn't open nul");
 
@@ -58,8 +58,8 @@
     Win32Fatal("SetHandleInformation");
 
   PROCESS_INFORMATION process_info = {};
-  STARTUPINFO startup_info = {};
-  startup_info.cb = sizeof(STARTUPINFO);
+  STARTUPINFOA startup_info = {};
+  startup_info.cb = sizeof(STARTUPINFOA);
   startup_info.hStdInput = nul;
   startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
   startup_info.hStdOutput = stdout_write;
diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc
index e419cd7..644b2a2 100644
--- a/src/msvc_helper_main-win32.cc
+++ b/src/msvc_helper_main-win32.cc
@@ -113,7 +113,7 @@
     PushPathIntoEnvironment(env);
   }
 
-  char* command = GetCommandLine();
+  char* command = GetCommandLineA();
   command = strstr(command, " -- ");
   if (!command) {
     Fatal("expected command line to end with \" -- command args\"");
diff --git a/src/ninja.cc b/src/ninja.cc
index 30f89c2..8108f21 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -154,7 +154,7 @@
     // Just checking n isn't enough: If an old output is both in the build log
     // and in the deps log, it will have a Node object in state_.  (It will also
     // have an in edge if one of its inputs is another output that's in the deps
-    // log, but having a deps edge product an output thats input to another deps
+    // log, but having a deps edge product an output that's input to another deps
     // edge is rare, and the first recompaction will delete all old outputs from
     // the deps log, and then a second recompaction will clear the build log,
     // which seems good enough for this corner case.)
@@ -207,7 +207,7 @@
 "  -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"
@@ -662,7 +662,65 @@
   }
 }
 
-int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* argv[]) {
+enum EvaluateCommandMode {
+  ECM_NORMAL,
+  ECM_EXPAND_RSPFILE
+};
+string EvaluateCommandWithRspfile(Edge* edge, EvaluateCommandMode mode) {
+  string command = edge->EvaluateCommand();
+  if (mode == ECM_NORMAL)
+    return command;
+
+  string rspfile = edge->GetUnescapedRspfile();
+  if (rspfile.empty())
+    return command;
+
+  size_t index = command.find(rspfile);
+  if (index == 0 || index == string::npos || command[index - 1] != '@')
+    return command;
+
+  string rspfile_content = edge->GetBinding("rspfile_content");
+  size_t newline_index = 0;
+  while ((newline_index = rspfile_content.find('\n', newline_index)) !=
+         string::npos) {
+    rspfile_content.replace(newline_index, 1, 1, ' ');
+    ++newline_index;
+  }
+  command.replace(index - 1, rspfile.length() + 1, rspfile_content);
+  return command;
+}
+
+int NinjaMain::ToolCompilationDatabase(const Options* options, int argc,
+                                       char* argv[]) {
+  // The compdb tool uses getopt, and expects argv[0] to contain the name of
+  // the tool, i.e. "compdb".
+  argc++;
+  argv--;
+
+  EvaluateCommandMode eval_mode = ECM_NORMAL;
+
+  optind = 1;
+  int opt;
+  while ((opt = getopt(argc, argv, const_cast<char*>("hx"))) != -1) {
+    switch(opt) {
+      case 'x':
+        eval_mode = ECM_EXPAND_RSPFILE;
+        break;
+
+      case 'h':
+      default:
+        printf(
+            "usage: ninja -t compdb [options] [rules]\n"
+            "\n"
+            "options:\n"
+            "  -x     expand @rspfile style response file invocations\n"
+            );
+        return 1;
+    }
+  }
+  argv += optind;
+  argc -= optind;
+
   bool first = true;
   vector<char> cwd;
 
@@ -688,9 +746,11 @@
         printf("\n  {\n    \"directory\": \"");
         EncodeJSONString(&cwd[0]);
         printf("\",\n    \"command\": \"");
-        EncodeJSONString((*e)->EvaluateCommand().c_str());
+        EncodeJSONString(EvaluateCommandWithRspfile(*e, eval_mode).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,17 +1178,20 @@
   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";
+  options.dupe_edges_should_err = true;
 
   setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
   const char* ninja_command = argv[0];
 
   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 +1210,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 +1229,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 +1278,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 +1286,6 @@
     return 2;
   }
 #else
-  return real_main(argc, argv);
+  real_main(argc, argv);
 #endif
 }
diff --git a/src/state.h b/src/state.h
index 54e9dc5..6fe886c 100644
--- a/src/state.h
+++ b/src/state.h
@@ -33,7 +33,7 @@
 /// Pools are scoped to a State. Edges within a State will share Pools. A Pool
 /// will keep a count of the total 'weight' of the currently scheduled edges. If
 /// a Plan attempts to schedule an Edge which would cause the total weight to
-/// exceed the depth of the Pool, the Pool will enque the Edge instead of
+/// exceed the depth of the Pool, the Pool will enqueue the Edge instead of
 /// allowing the Plan to schedule it. The Pool will relinquish queued Edges when
 /// the total scheduled weight diminishes enough (i.e. when a scheduled edge
 /// completes).
diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc
index 4bab719..5982b06 100644
--- a/src/subprocess-win32.cc
+++ b/src/subprocess-win32.cc
@@ -59,8 +59,8 @@
   }
 
   // Get the write end of the pipe as a handle inheritable across processes.
-  HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0,
-                                          NULL, OPEN_EXISTING, 0, NULL);
+  HANDLE output_write_handle =
+      CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
   HANDLE output_write_child;
   if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
                        GetCurrentProcess(), &output_write_child,
@@ -80,9 +80,10 @@
   security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
   security_attributes.bInheritHandle = TRUE;
   // Must be inheritable so subprocesses can dup to children.
-  HANDLE nul = CreateFile("NUL", GENERIC_READ,
-          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-          &security_attributes, OPEN_EXISTING, 0, NULL);
+  HANDLE nul =
+      CreateFileA("NUL", GENERIC_READ,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  &security_attributes, OPEN_EXISTING, 0, NULL);
   if (nul == INVALID_HANDLE_VALUE)
     Fatal("couldn't open nul");
 
diff --git a/src/test.h b/src/test.h
index 3bce8f7..6af17b3 100644
--- a/src/test.h
+++ b/src/test.h
@@ -104,7 +104,7 @@
     }                                                        \
   }
 
-// Support utilites for tests.
+// Support utilities for tests.
 
 struct Node;
 
diff --git a/src/util.cc b/src/util.cc
index ae94d34..760bc23 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -197,7 +197,7 @@
       case '\\':
         bits |= bits_mask;
         *c = '/';
-        // Intentional fallthrough.
+        NINJA_FALLTHROUGH;
       case '/':
         bits_mask <<= 1;
     }
@@ -318,13 +318,8 @@
   // This makes a ninja run on a set of 1500 manifest files about 4% faster
   // than using the generic fopen code below.
   err->clear();
-  HANDLE f = ::CreateFile(path.c_str(),
-                          GENERIC_READ,
-                          FILE_SHARE_READ,
-                          NULL,
-                          OPEN_EXISTING,
-                          FILE_FLAG_SEQUENTIAL_SCAN,
-                          NULL);
+  HANDLE f = ::CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
+                           OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
   if (f == INVALID_HANDLE_VALUE) {
     err->assign(GetLastErrorString());
     return -ENOENT;
diff --git a/src/util.h b/src/util.h
index 4ee41a5..1b4227c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -34,6 +34,20 @@
 /// Log a fatal message and exit.
 NORETURN void Fatal(const char* msg, ...);
 
+// Have a generic fall-through for different versions of C/C++.
+#if defined(__cplusplus) && __cplusplus >= 201703L
+#define NINJA_FALLTHROUGH [[fallthrough]]
+#elif defined(__cplusplus) && __cplusplus >= 201103L && defined(__clang__)
+#define NINJA_FALLTHROUGH [[clang::fallthrough]]
+#elif defined(__cplusplus) && __cplusplus >= 201103L && defined(__GNUC__) && \
+    __GNUC__ >= 7
+#define NINJA_FALLTHROUGH [[gnu::fallthrough]]
+#elif defined(__GNUC__) && __GNUC__ >= 7 // gcc 7
+#define NINJA_FALLTHROUGH __attribute__ ((fallthrough))
+#else // C++11 on gcc 6, and all other cases
+#define NINJA_FALLTHROUGH
+#endif
+
 /// Log a warning message.
 void Warning(const char* msg, ...);