Merge pull request #1205 from nico/clangclformat

fix a clang-cl -Wformat warning
diff --git a/RELEASING b/RELEASING
index 20da5d9..5f51b73 100644
--- a/RELEASING
+++ b/RELEASING
@@ -2,17 +2,18 @@
 
 Push new release branch:
 1. Consider sending a heads-up to the ninja-build mailing list first
-2. update src/version.cc with new version (with ".git"), then
-       git commit -a -m 'mark this 1.5.0.git'
-3. git checkout release; git merge master
-4. fix version number in src/version.cc (it will likely conflict in the above)
-5. fix version in doc/manual.asciidoc
-6. commit, tag, push (don't forget to push --tags)
-       git commit -a -m v1.5.0; git push origin release
+2. Make sure branches 'master' and 'release' are synced up locally
+3. 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)
+       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
-7. construct release notes from prior notes
+8. construct release notes from prior notes
    credits: git shortlog -s --no-merges REV..
 
 Release on github:
@@ -25,7 +26,7 @@
 Update website:
 1. Make sure your ninja checkout is on the v1.5.0 tag
 2. Clone https://github.com/ninja-build/ninja-build.github.io
-3. In that repo, `cd ninja && ./update-docs.sh`
+3. In that repo, `./update-docs.sh`
 4. Update index.html with newest version and link to release notes
 5. git commit -m 'run update-docs.sh, 1.5.0 release'
 6. git push origin master
diff --git a/configure.py b/configure.py
index 84218b9..9ec368f 100755
--- a/configure.py
+++ b/configure.py
@@ -98,7 +98,7 @@
         return self._platform in ('freebsd', 'openbsd', 'bitrig')
 
     def supports_ppoll(self):
-        return self._platform in ('linux', 'openbsd', 'bitrig')
+        return self._platform in ('freebsd', 'linux', 'openbsd', 'bitrig')
 
     def supports_ninja_browse(self):
         return (not self.is_windows()
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 9fc5fb9..d7ec932 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -208,7 +208,7 @@
 `%e`:: Elapsed time in seconds.  _(Available since Ninja 1.2.)_
 `%%`:: A plain `%` character.
 
-The default progress status is `"[%s/%t] "` (note the trailing space
+The default progress status is `"[%f/%t] "` (note the trailing space
 to separate from the build rule). Another example of possible progress status
 could be `"[%u/%r/%f] "`.
 
diff --git a/misc/measure.py b/misc/measure.py
index 1323fc6..8ce95e6 100755
--- a/misc/measure.py
+++ b/misc/measure.py
@@ -17,6 +17,8 @@
 """measure the runtime of a command by repeatedly running it.
 """
 
+from __future__ import print_function
+
 import time
 import subprocess
 import sys
@@ -24,7 +26,7 @@
 devnull = open('/dev/null', 'w')
 
 def run(cmd, repeat=10):
-    print 'sampling:',
+    print('sampling:', end=' ')
     sys.stdout.flush()
 
     samples = []
@@ -33,10 +35,10 @@
         subprocess.call(cmd, stdout=devnull, stderr=devnull)
         end = time.time()
         dt = (end - start) * 1000
-        print '%dms' % int(dt),
+        print('%dms' % int(dt), end=' ')
         sys.stdout.flush()
         samples.append(dt)
-    print
+    print()
 
     # We're interested in the 'pure' runtime of the code, which is
     # conceptually the smallest time we'd see if we ran it enough times
@@ -45,10 +47,10 @@
     # Also print how varied the outputs were in an attempt to make it
     # more obvious if something has gone terribly wrong.
     err = sum(s - best for s in samples) / float(len(samples))
-    print 'estimate: %dms (mean err %.1fms)' % (best, err)
+    print('estimate: %dms (mean err %.1fms)' % (best, err))
 
 if __name__ == '__main__':
     if len(sys.argv) < 2:
-        print 'usage: measure.py command args...'
+        print('usage: measure.py command args...')
         sys.exit(1)
     run(cmd=sys.argv[1:])
diff --git a/misc/write_fake_manifests.py b/misc/write_fake_manifests.py
index ca49535..b3594de 100644
--- a/misc/write_fake_manifests.py
+++ b/misc/write_fake_manifests.py
@@ -50,9 +50,10 @@
 
 
 class GenRandom(object):
-    def __init__(self):
+    def __init__(self, src_dir):
         self.seen_names = set([None])
         self.seen_defines = set([None])
+        self.src_dir = src_dir
 
     def _unique_string(self, seen, avg_options=1.3, p_suffix=0.1):
         s = None
@@ -76,7 +77,7 @@
 
     def src_obj_pairs(self, path, name):
         num_sources = paretoint(55, alpha=2) + 1
-        return [(os.path.join('..', '..', path, s + '.cc'),
+        return [(os.path.join(self.src_dir, path, s + '.cc'),
                  os.path.join('obj', path, '%s.%s.o' % (name, s)))
                 for s in self._n_unique_strings(num_sources)]
 
@@ -103,12 +104,8 @@
         self.kind = kind
         self.has_compile_depends = random.random() < 0.4
 
-    @property
-    def includes(self):
-        return ['-I' + dep.dir_path for dep in self.deps]
 
-
-def write_target_ninja(ninja, target):
+def write_target_ninja(ninja, target, src_dir):
     compile_depends = None
     if target.has_compile_depends:
       compile_depends = os.path.join(
@@ -117,8 +114,7 @@
       ninja.newline()
 
     ninja.variable('defines', target.defines)
-    if target.deps:
-        ninja.variable('includes', target.includes)
+    ninja.variable('includes', '-I' + src_dir)
     ninja.variable('cflags', ['-Wall', '-fno-rtti', '-fno-exceptions'])
     ninja.newline()
 
@@ -129,17 +125,63 @@
     deps = [dep.output for dep in target.deps]
     libs = [dep.output for dep in target.deps if dep.kind == LIB]
     if target.kind == EXE:
-        ninja.variable('ldflags', '-Wl,pie')
         ninja.variable('libs', libs)
+        if sys.platform == "darwin":
+            ninja.variable('ldflags', '-Wl,-pie')
     link = { LIB: 'alink', EXE: 'link'}[target.kind]
     ninja.build(target.output, link, [obj for _, obj in target.src_obj_pairs],
                 implicit=deps)
 
 
+def write_sources(target, root_dir):
+    need_main = target.kind == EXE
+
+    includes = []
+
+    # Include siblings.
+    for cc_filename, _ in target.src_obj_pairs:
+        h_filename = os.path.basename(os.path.splitext(cc_filename)[0] + '.h')
+        includes.append(h_filename)
+
+    # Include deps.
+    for dep in target.deps:
+        for cc_filename, _ in dep.src_obj_pairs:
+            h_filename = os.path.basename(
+                os.path.splitext(cc_filename)[0] + '.h')
+            includes.append("%s/%s" % (dep.dir_path, h_filename))
+
+    for cc_filename, _ in target.src_obj_pairs:
+        cc_path = os.path.join(root_dir, cc_filename)
+        h_path = os.path.splitext(cc_path)[0] + '.h'
+        namespace = os.path.basename(target.dir_path)
+        class_ = os.path.splitext(os.path.basename(cc_filename))[0]
+        try:
+            os.makedirs(os.path.dirname(cc_path))
+        except OSError:
+            pass
+
+        with open(h_path, 'w') as f:
+            f.write('namespace %s { struct %s { %s(); }; }' % (namespace,
+                                                               class_, class_))
+        with open(cc_path, 'w') as f:
+            for include in includes:
+                f.write('#include "%s"\n' % include)
+            f.write('\n')
+            f.write('namespace %s { %s::%s() {} }' % (namespace,
+                                                      class_, class_))
+
+            if need_main:
+                f.write('int main(int argc, char **argv) {}\n')
+                need_main = False
+
 def write_master_ninja(master_ninja, targets):
     """Writes master build.ninja file, referencing all given subninjas."""
     master_ninja.variable('cxx', 'c++')
     master_ninja.variable('ld', '$cxx')
+    if sys.platform == 'darwin':
+        master_ninja.variable('alink', 'libtool -static')
+    else:
+        master_ninja.variable('alink', 'ar rcs')
     master_ninja.newline()
 
     master_ninja.pool('link_pool', depth=4)
@@ -148,8 +190,8 @@
     master_ninja.rule('cxx', description='CXX $out',
       command='$cxx -MMD -MF $out.d $defines $includes $cflags -c $in -o $out',
       depfile='$out.d', deps='gcc')
-    master_ninja.rule('alink', description='LIBTOOL-STATIC $out',
-      command='rm -f $out && libtool -static -o $out $in')
+    master_ninja.rule('alink', description='ARCHIVE $out',
+      command='rm -f $out && $alink -o $out $in')
     master_ninja.rule('link', description='LINK $out', pool='link_pool',
       command='$ld $ldflags -o $out $in $libs')
     master_ninja.rule('stamp', description='STAMP $out', command='touch $out')
@@ -181,9 +223,8 @@
     f.close()
 
 
-def random_targets():
-    num_targets = 1500
-    gen = GenRandom()
+def random_targets(num_targets, src_dir):
+    gen = GenRandom(src_dir)
 
     # N-1 static libraries, and 1 executable depending on all of them.
     targets = [Target(gen, LIB) for i in xrange(num_targets - 1)]
@@ -199,16 +240,28 @@
 
 def main():
     parser = argparse.ArgumentParser()
+    parser.add_argument('-s', '--sources', nargs="?", const="src",
+        help='write sources to directory (relative to output directory)')
+    parser.add_argument('-t', '--targets', type=int, default=1500,
+                        help='number of targets (default: 1500)')
+    parser.add_argument('-S', '--seed', type=int, help='random seed',
+                        default=12345)
     parser.add_argument('outdir', help='output directory')
     args = parser.parse_args()
     root_dir = args.outdir
 
-    random.seed(12345)
+    random.seed(args.seed)
 
-    targets = random_targets()
+    do_write_sources = args.sources is not None
+    src_dir = args.sources if do_write_sources else "src"
+
+    targets = random_targets(args.targets, src_dir)
     for target in targets:
         with FileWriter(os.path.join(root_dir, target.ninja_file_path)) as n:
-            write_target_ninja(n, target)
+            write_target_ninja(n, target, src_dir)
+
+        if do_write_sources:
+            write_sources(target, root_dir)
 
     with FileWriter(os.path.join(root_dir, 'build.ninja')) as master_ninja:
         master_ninja.width = 120
diff --git a/src/browse.py b/src/browse.py
index 32792f3..4b4faa8 100755
--- a/src/browse.py
+++ b/src/browse.py
@@ -27,6 +27,7 @@
 except ImportError:
     import BaseHTTPServer as httpserver
 import argparse
+import cgi
 import os
 import socket
 import subprocess
@@ -58,6 +59,9 @@
         return (False, line)
     return (True, line[len(prefix):])
 
+def html_escape(text):
+    return cgi.escape(text, quote=True)
+
 def parse(text):
     lines = iter(text.split('\n'))
 
@@ -124,19 +128,19 @@
 ''' + body
 
 def generate_html(node):
-    document = ['<h1><tt>%s</tt></h1>' % node.target]
+    document = ['<h1><tt>%s</tt></h1>' % html_escape(node.target)]
 
     if node.inputs:
         document.append('<h2>target is built using rule <tt>%s</tt> of</h2>' %
-                        node.rule)
+                        html_escape(node.rule))
         if len(node.inputs) > 0:
             document.append('<div class=filelist>')
             for input, type in sorted(node.inputs):
                 extra = ''
                 if type:
-                    extra = ' (%s)' % type
+                    extra = ' (%s)' % html_escape(type)
                 document.append('<tt><a href="?%s">%s</a>%s</tt><br>' %
-                                (input, input, extra))
+                                (html_escape(input), html_escape(input), extra))
             document.append('</div>')
 
     if node.outputs:
@@ -144,7 +148,7 @@
         document.append('<div class=filelist>')
         for output in sorted(node.outputs):
             document.append('<tt><a href="?%s">%s</a></tt><br>' %
-                            (output, output))
+                            (html_escape(output), html_escape(output)))
         document.append('</div>')
 
     return '\n'.join(document)
@@ -177,7 +181,7 @@
             page_body = generate_html(parse(ninja_output.strip()))
         else:
             # Relay ninja's error message.
-            page_body = '<h1><tt>%s</tt></h1>' % ninja_error
+            page_body = '<h1><tt>%s</tt></h1>' % html_escape(ninja_error)
 
         self.send_response(200)
         self.end_headers()
diff --git a/src/build.cc b/src/build.cc
index 3a17bdb..64710dd 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -84,7 +84,7 @@
 
   progress_status_format_ = getenv("NINJA_STATUS");
   if (!progress_status_format_)
-    progress_status_format_ = "[%s/%t] ";
+    progress_status_format_ = "[%f/%t] ";
 }
 
 void BuildStatus::PlanHasTotalEdges(int total) {
@@ -97,7 +97,7 @@
   ++started_edges_;
 
   if (edge->use_console() || printer_.is_smart_terminal())
-    PrintStatus(edge);
+    PrintStatus(edge, kEdgeStarted);
 
   if (edge->use_console())
     printer_.SetConsoleLocked(true);
@@ -109,6 +109,7 @@
                                     int* start_time,
                                     int* end_time) {
   int64_t now = GetTimeMillis();
+
   ++finished_edges_;
 
   RunningEdgeMap::iterator i = running_edges_.find(edge);
@@ -123,7 +124,7 @@
     return;
 
   if (!edge->use_console())
-    PrintStatus(edge);
+    PrintStatus(edge, kEdgeFinished);
 
   // Print the command that is spewing before printing its output.
   if (!success) {
@@ -158,13 +159,18 @@
   }
 }
 
+void BuildStatus::BuildStarted() {
+  overall_rate_.Restart();
+  current_rate_.Restart();
+}
+
 void BuildStatus::BuildFinished() {
   printer_.SetConsoleLocked(false);
   printer_.PrintOnNewLine("");
 }
 
 string BuildStatus::FormatProgressStatus(
-    const char* progress_status_format) const {
+    const char* progress_status_format, EdgeStatus status) const {
   string out;
   char buf[32];
   int percent;
@@ -189,10 +195,15 @@
         break;
 
         // Running edges.
-      case 'r':
-        snprintf(buf, sizeof(buf), "%d", started_edges_ - finished_edges_);
+      case 'r': {
+        int running_edges = started_edges_ - finished_edges_;
+        // count the edge that just finished as a running edge
+        if (status == kEdgeFinished)
+          running_edges++;
+        snprintf(buf, sizeof(buf), "%d", running_edges);
         out += buf;
         break;
+      }
 
         // Unstarted edges.
       case 'u':
@@ -209,20 +220,20 @@
         // Overall finished edges per second.
       case 'o':
         overall_rate_.UpdateRate(finished_edges_);
-        snprinfRate(overall_rate_.rate(), buf, "%.1f");
+        SnprintfRate(overall_rate_.rate(), buf, "%.1f");
         out += buf;
         break;
 
         // Current rate, average over the last '-j' jobs.
       case 'c':
         current_rate_.UpdateRate(finished_edges_);
-        snprinfRate(current_rate_.rate(), buf, "%.1f");
+        SnprintfRate(current_rate_.rate(), buf, "%.1f");
         out += buf;
         break;
 
         // Percentage
       case 'p':
-        percent = (100 * started_edges_) / total_edges_;
+        percent = (100 * finished_edges_) / total_edges_;
         snprintf(buf, sizeof(buf), "%3i%%", percent);
         out += buf;
         break;
@@ -246,7 +257,7 @@
   return out;
 }
 
-void BuildStatus::PrintStatus(Edge* edge) {
+void BuildStatus::PrintStatus(Edge* edge, EdgeStatus status) {
   if (config_.verbosity == BuildConfig::QUIET)
     return;
 
@@ -256,11 +267,7 @@
   if (to_print.empty() || force_full_command)
     to_print = edge->GetBinding("command");
 
-  if (finished_edges_ == 0) {
-    overall_rate_.Restart();
-    current_rate_.Restart();
-  }
-  to_print = FormatProgressStatus(progress_status_format_) + to_print;
+  to_print = FormatProgressStatus(progress_status_format_, status) + to_print;
 
   printer_.Print(to_print,
                  force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE);
@@ -643,6 +650,9 @@
       command_runner_.reset(new RealCommandRunner(config_));
   }
 
+  // We are about to start the build process.
+  status_->BuildStarted();
+
   // This main loop runs the entire build process.
   // It is structured like this:
   // First, we attempt to start as many commands as allowed by the
diff --git a/src/build.h b/src/build.h
index 51589ef..66ce607 100644
--- a/src/build.h
+++ b/src/build.h
@@ -200,16 +200,24 @@
   void BuildEdgeStarted(Edge* edge);
   void BuildEdgeFinished(Edge* edge, bool success, const string& output,
                          int* start_time, int* end_time);
+  void BuildStarted();
   void BuildFinished();
 
+  enum EdgeStatus {
+    kEdgeStarted,
+    kEdgeFinished,
+  };
+
   /// Format the progress status string by replacing the placeholders.
   /// See the user manual for more information about the available
   /// placeholders.
   /// @param progress_status_format The format of the progress status.
-  string FormatProgressStatus(const char* progress_status_format) const;
+  /// @param status The status of the edge.
+  string FormatProgressStatus(const char* progress_status_format,
+                              EdgeStatus status) const;
 
  private:
-  void PrintStatus(Edge* edge);
+  void PrintStatus(Edge* edge, EdgeStatus status);
 
   const BuildConfig& config_;
 
@@ -229,9 +237,11 @@
   const char* progress_status_format_;
 
   template<size_t S>
-  void snprinfRate(double rate, char(&buf)[S], const char* format) const {
-    if (rate == -1) snprintf(buf, S, "?");
-    else            snprintf(buf, S, format, rate);
+  void SnprintfRate(double rate, char(&buf)[S], const char* format) const {
+    if (rate == -1)
+      snprintf(buf, S, "?");
+    else
+      snprintf(buf, S, format, rate);
   }
 
   struct RateInfo {
diff --git a/src/build_test.cc b/src/build_test.cc
index 55d093e..640e1b0 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -1286,7 +1286,8 @@
   ASSERT_EQ("", err);
   EXPECT_TRUE(builder_.Build(&err));
   ASSERT_EQ("", err);
-  EXPECT_EQ("[3/3]", builder_.status_->FormatProgressStatus("[%s/%t]"));
+  EXPECT_EQ("[3/3]", builder_.status_->FormatProgressStatus("[%s/%t]",
+      BuildStatus::kEdgeStarted));
   command_runner_.commands_ran_.clear();
   state_.Reset();
 
@@ -1724,9 +1725,18 @@
   ASSERT_EQ(1u, command_runner_.commands_ran_.size());
 }
 
+TEST_F(BuildTest, StatusFormatElapsed) {
+  status_.BuildStarted();
+  // Before any task is done, the elapsed time must be zero.
+  EXPECT_EQ("[%/e0.000]",
+            status_.FormatProgressStatus("[%%/e%e]",
+                BuildStatus::kEdgeStarted));
+}
+
 TEST_F(BuildTest, StatusFormatReplacePlaceholder) {
   EXPECT_EQ("[%/s0/t0/r0/u0/f0]",
-            status_.FormatProgressStatus("[%%/s%s/t%t/r%r/u%u/f%f]"));
+            status_.FormatProgressStatus("[%%/s%s/t%t/r%r/u%u/f%f]",
+                BuildStatus::kEdgeStarted));
 }
 
 TEST_F(BuildTest, FailedDepsParse) {
diff --git a/src/graph.cc b/src/graph.cc
index f1d9ca2..136ca04 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -422,8 +422,10 @@
 
   unsigned int unused;
   if (!CanonicalizePath(const_cast<char*>(depfile.out_.str_),
-                        &depfile.out_.len_, &unused, err))
+                        &depfile.out_.len_, &unused, err)) {
+    *err = path + ": " + *err;
     return false;
+  }
 
   // Check that this depfile matches the edge's output, if not return false to
   // mark the edge as dirty.
diff --git a/src/graph_test.cc b/src/graph_test.cc
index 723e8ea..be08b19 100644
--- a/src/graph_test.cc
+++ b/src/graph_test.cc
@@ -149,6 +149,45 @@
   EXPECT_TRUE(GetNode("out.imp")->dirty());
 }
 
+TEST_F(GraphTest, ImplicitOutputOnlyParse) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build | out.imp: cat in\n"));
+
+  Edge* edge = GetNode("out.imp")->in_edge();
+  EXPECT_EQ(1, edge->outputs_.size());
+  EXPECT_EQ("out.imp", edge->outputs_[0]->path());
+  EXPECT_EQ(1, edge->implicit_outs_);
+  EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
+}
+
+TEST_F(GraphTest, ImplicitOutputOnlyMissing) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build | out.imp: cat in\n"));
+  fs_.Create("in", "");
+
+  Edge* edge = GetNode("out.imp")->in_edge();
+  string err;
+  EXPECT_TRUE(scan_.RecomputeDirty(edge, &err));
+  ASSERT_EQ("", err);
+
+  EXPECT_TRUE(GetNode("out.imp")->dirty());
+}
+
+TEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build | out.imp: cat in\n"));
+  fs_.Create("out.imp", "");
+  fs_.Tick();
+  fs_.Create("in", "");
+
+  Edge* edge = GetNode("out.imp")->in_edge();
+  string err;
+  EXPECT_TRUE(scan_.RecomputeDirty(edge, &err));
+  ASSERT_EQ("", err);
+
+  EXPECT_TRUE(GetNode("out.imp")->dirty());
+}
+
 TEST_F(GraphTest, PathWithCurrentDirectory) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
 "rule catdep\n"
diff --git a/src/hash_collision_bench.cc b/src/hash_collision_bench.cc
index 5be0531..ff947dc 100644
--- a/src/hash_collision_bench.cc
+++ b/src/hash_collision_bench.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 using namespace std;
 
+#include <stdlib.h>
 #include <time.h>
 
 int random(int low, int high) {
diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc
index be267a3..d6dcf22 100644
--- a/src/manifest_parser.cc
+++ b/src/manifest_parser.cc
@@ -236,16 +236,13 @@
     EvalString out;
     if (!lexer_.ReadPath(&out, err))
       return false;
-    if (out.empty())
-      return lexer_.Error("expected path", err);
-
-    do {
+    while (!out.empty()) {
       outs.push_back(out);
 
       out.Clear();
       if (!lexer_.ReadPath(&out, err))
         return false;
-    } while (!out.empty());
+    }
   }
 
   // Add all implicit outs, counting how many as we go.
@@ -262,6 +259,9 @@
     }
   }
 
+  if (outs.empty())
+    return lexer_.Error("expected path", err);
+
   if (!ExpectToken(Lexer::COLON, err))
     return false;
 
@@ -339,8 +339,8 @@
   }
 
   edge->outputs_.reserve(outs.size());
-  for (vector<EvalString>::iterator i = outs.begin(); i != outs.end(); ++i) {
-    string path = i->Evaluate(env);
+  for (size_t i = 0, e = outs.size(); i != e; ++i) {
+    string path = outs[i].Evaluate(env);
     string path_err;
     unsigned int slash_bits;
     if (!CanonicalizePath(&path, &slash_bits, &path_err))
@@ -350,11 +350,15 @@
         lexer_.Error("multiple rules generate " + path + " [-w dupbuild=err]",
                      err);
         return false;
-      } else if (!quiet_) {
-        Warning("multiple rules generate %s. "
-                "builds involving this target will not be correct; "
-                "continuing anyway [-w dupbuild=warn]",
-                path.c_str());
+      } else {
+        if (!quiet_) {
+          Warning("multiple rules generate %s. "
+                  "builds involving this target will not be correct; "
+                  "continuing anyway [-w dupbuild=warn]",
+                  path.c_str());
+        }
+        if (e - i <= static_cast<size_t>(implicit_outs))
+          --implicit_outs;
       }
     }
   }
diff --git a/src/manifest_parser_perftest.cc b/src/manifest_parser_perftest.cc
index 572e2d5..60c2054 100644
--- a/src/manifest_parser_perftest.cc
+++ b/src/manifest_parser_perftest.cc
@@ -19,6 +19,7 @@
 
 #include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #ifdef _WIN32
diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index ba83a67..3c82dc5 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -949,16 +949,37 @@
   EXPECT_FALSE(edge->is_implicit_out(0));
 }
 
+TEST_F(ParserTest, ImplicitOutputDupe) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(
+"rule cat\n"
+"  command = cat $in > $out\n"
+"build foo baz | foo baq foo: cat bar\n"));
+
+  Edge* edge = state.LookupNode("foo")->in_edge();
+  ASSERT_EQ(edge->outputs_.size(), 3);
+  EXPECT_FALSE(edge->is_implicit_out(0));
+  EXPECT_FALSE(edge->is_implicit_out(1));
+  EXPECT_TRUE(edge->is_implicit_out(2));
+}
+
+TEST_F(ParserTest, ImplicitOutputDupes) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(
+"rule cat\n"
+"  command = cat $in > $out\n"
+"build foo foo foo | foo foo foo foo: cat bar\n"));
+
+  Edge* edge = state.LookupNode("foo")->in_edge();
+  ASSERT_EQ(edge->outputs_.size(), 1);
+  EXPECT_FALSE(edge->is_implicit_out(0));
+}
+
 TEST_F(ParserTest, NoExplicitOutput) {
   ManifestParser parser(&state, NULL, kDupeEdgeActionWarn);
   string err;
-  EXPECT_FALSE(parser.ParseTest(
+  EXPECT_TRUE(parser.ParseTest(
 "rule cat\n"
 "  command = cat $in > $out\n"
 "build | imp : cat bar\n", &err));
-  ASSERT_EQ("input:3: expected path\n"
-            "build | imp : cat bar\n"
-            "      ^ near here", err);
 }
 
 TEST_F(ParserTest, DefaultDefault) {
diff --git a/src/ninja.cc b/src/ninja.cc
index 25eafe8..5ab73e9 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -533,21 +533,51 @@
   }
 }
 
-void PrintCommands(Edge* edge, set<Edge*>* seen) {
+enum PrintCommandMode { PCM_Single, PCM_All };
+void PrintCommands(Edge* edge, set<Edge*>* seen, PrintCommandMode mode) {
   if (!edge)
     return;
   if (!seen->insert(edge).second)
     return;
 
-  for (vector<Node*>::iterator in = edge->inputs_.begin();
-       in != edge->inputs_.end(); ++in)
-    PrintCommands((*in)->in_edge(), seen);
+  if (mode == PCM_All) {
+    for (vector<Node*>::iterator in = edge->inputs_.begin();
+         in != edge->inputs_.end(); ++in)
+      PrintCommands((*in)->in_edge(), seen, mode);
+  }
 
   if (!edge->is_phony())
     puts(edge->EvaluateCommand().c_str());
 }
 
 int NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) {
+  // The clean tool uses getopt, and expects argv[0] to contain the name of
+  // the tool, i.e. "commands".
+  ++argc;
+  --argv;
+
+  PrintCommandMode mode = PCM_All;
+
+  optind = 1;
+  int opt;
+  while ((opt = getopt(argc, argv, const_cast<char*>("hs"))) != -1) {
+    switch (opt) {
+    case 's':
+      mode = PCM_Single;
+      break;
+    case 'h':
+    default:
+      printf("usage: ninja -t commands [options] [targets]\n"
+"\n"
+"options:\n"
+"  -s     only print the final command to build [target], not the whole chain\n"
+             );
+    return 1;
+    }
+  }
+  argv += optind;
+  argc -= optind;
+
   vector<Node*> nodes;
   string err;
   if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
@@ -557,7 +587,7 @@
 
   set<Edge*> seen;
   for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in)
-    PrintCommands((*in)->in_edge(), &seen);
+    PrintCommands((*in)->in_edge(), &seen, mode);
 
   return 0;
 }
diff --git a/src/ninja_test.cc b/src/ninja_test.cc
index 11087b6..d642c5c 100644
--- a/src/ninja_test.cc
+++ b/src/ninja_test.cc
@@ -14,6 +14,7 @@
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #ifdef _WIN32
 #include "getopt.h"
diff --git a/src/state.cc b/src/state.cc
index a70f211..d539e7b 100644
--- a/src/state.cc
+++ b/src/state.cc
@@ -159,11 +159,12 @@
   return true;
 }
 
-vector<Node*> State::RootNodes(string* err) {
+vector<Node*> State::RootNodes(string* err) const {
   vector<Node*> root_nodes;
   // Search for nodes with no output.
-  for (vector<Edge*>::iterator e = edges_.begin(); e != edges_.end(); ++e) {
-    for (vector<Node*>::iterator out = (*e)->outputs_.begin();
+  for (vector<Edge*>::const_iterator e = edges_.begin();
+       e != edges_.end(); ++e) {
+    for (vector<Node*>::const_iterator out = (*e)->outputs_.begin();
          out != (*e)->outputs_.end(); ++out) {
       if ((*out)->out_edges().empty())
         root_nodes.push_back(*out);
@@ -176,7 +177,7 @@
   return root_nodes;
 }
 
-vector<Node*> State::DefaultNodes(string* err) {
+vector<Node*> State::DefaultNodes(string* err) const {
   return defaults_.empty() ? RootNodes(err) : defaults_;
 }
 
diff --git a/src/state.h b/src/state.h
index d7987ba..b530207 100644
--- a/src/state.h
+++ b/src/state.h
@@ -110,8 +110,8 @@
 
   /// @return the root node(s) of the graph. (Root nodes have no output edges).
   /// @param error where to write the error message if somethings went wrong.
-  vector<Node*> RootNodes(string* error);
-  vector<Node*> DefaultNodes(string* error);
+  vector<Node*> RootNodes(string* error) const;
+  vector<Node*> DefaultNodes(string* error) const;
 
   /// Mapping of path -> Node.
   typedef ExternalStringHashMap<Node*>::Type Paths;
diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc
index 5ffe85b..f1f94e5 100644
--- a/src/subprocess-posix.cc
+++ b/src/subprocess-posix.cc
@@ -73,8 +73,6 @@
   // default action in the new process image, so no explicit
   // POSIX_SPAWN_SETSIGDEF parameter is needed.
 
-  // TODO: Consider using POSIX_SPAWN_USEVFORK on Linux with glibc?
-
   if (!use_console_) {
     // Put the child in its own process group, so ctrl-c won't reach it.
     flags |= POSIX_SPAWN_SETPGROUP;
@@ -93,6 +91,9 @@
     // In the console case, output_pipe is still inherited by the child and
     // closed when the subprocess finishes, which then notifies ninja.
   }
+#ifdef POSIX_SPAWN_USEVFORK
+  flags |= POSIX_SPAWN_USEVFORK;
+#endif
 
   if (posix_spawnattr_setflags(&attr, flags) != 0)
     Fatal("posix_spawnattr_setflags: %s", strerror(errno));
diff --git a/src/subprocess.h b/src/subprocess.h
index 51f40b2..b2d486c 100644
--- a/src/subprocess.h
+++ b/src/subprocess.h
@@ -26,6 +26,14 @@
 #include <signal.h>
 #endif
 
+// ppoll() exists on FreeBSD, but only on newer versions.
+#ifdef __FreeBSD__
+#  include <sys/param.h>
+#  if defined USE_PPOLL && __FreeBSD_version < 1002000
+#    undef USE_PPOLL
+#  endif
+#endif
+
 #include "exit_status.h"
 
 /// Subprocess wraps a single async subprocess.  It is entirely
diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
index ee16190..0a8c206 100644
--- a/src/subprocess_test.cc
+++ b/src/subprocess_test.cc
@@ -214,9 +214,7 @@
   }
 }
 
-// OS X's process limit is less than 1025 by default
-// (|sysctl kern.maxprocperuid| is 709 on 10.7 and 10.8 and less prior to that).
-#if !defined(__APPLE__) && !defined(_WIN32)
+#if defined(USE_PPOLL)
 TEST_F(SubprocessTest, SetWithLots) {
   // Arbitrary big number; needs to be over 1024 to confirm we're no longer
   // hostage to pselect.
diff --git a/src/test.cc b/src/test.cc
index 53bfc48..51882f0 100644
--- a/src/test.cc
+++ b/src/test.cc
@@ -21,6 +21,7 @@
 #include <algorithm>
 
 #include <errno.h>
+#include <stdlib.h>
 #ifdef _WIN32
 #include <windows.h>
 #else
diff --git a/src/version.cc b/src/version.cc
index 4c2aea1..e2a83bb 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -18,7 +18,7 @@
 
 #include "util.h"
 
-const char* kNinjaVersion = "1.6.0.git";
+const char* kNinjaVersion = "1.7.2.git";
 
 void ParseVersion(const string& version, int* major, int* minor) {
   size_t end = version.find('.');