Merge pull request #973 from sgraham/canonicalize-fix
authorNico Weber <nicolasweber@gmx.de>
Sun, 5 Jul 2015 22:53:46 +0000 (15:53 -0700)
committerNico Weber <nicolasweber@gmx.de>
Sun, 5 Jul 2015 22:53:46 +0000 (15:53 -0700)
Fix crash in attempting to canonicalize paths longer than _MAX_PATH

HACKING.md
README
configure.py
doc/manual.asciidoc
src/browse.cc
src/graph.cc
src/graph.h
src/version.cc

index 9c6830f..7fdb152 100644 (file)
@@ -2,10 +2,25 @@
 
 `./configure.py` generates the `build.ninja` files used to build
 ninja.  It accepts various flags to adjust build parameters.
+Run './configure.py --help' for more configuration options.
 
 The primary build target of interest is `ninja`, but when hacking on
-Ninja your changes should be testable so it's more useful to build
-and run `ninja_test` when developing.
+Ninja your changes should be testable so it's more useful to build and
+run `ninja_test` when developing.
+
+### Bootstrapping
+
+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.
+
+#### Windows
+
+On Windows, you'll need to install Python to run `configure.py`, and
+run everything under a Visual Studio Tools Command Prompt (or after
+running `vcvarsall` in a normal command prompt).  See below if you
+want to use mingw or some other compiler instead using Visual Studio.
 
 ### Adjusting build flags
 
@@ -176,7 +191,7 @@ root directory:
     ./ninja_test
     gcov build/*.o
 
-Look at the generated `.gcov` files directly, or use your favorit gcov viewer.
+Look at the generated `.gcov` files directly, or use your favorite gcov viewer.
 
 ### Using afl-fuzz
 
diff --git a/README b/README
index 41ecdda..66a6516 100644 (file)
--- a/README
+++ b/README
@@ -5,13 +5,14 @@ See the manual -- http://martine.github.com/ninja/manual.html or
 doc/manual.asciidoc included in the distribution -- for background
 and more details.
 
-To build, run ./configure.py --bootstrap.  It first compiles all non-test
-source files together, then re-builds Ninja using itself.  You should
-end up with a 'ninja' binary in the source root.
-
-Run './configure.py --help' for more configuration options.
+Binaries for Linux, Mac, and Windows are available at
+  https://github.com/martine/ninja/releases
 Run './ninja -h' for Ninja help.
 
+To build your own binary, on many platforms it should be sufficient to
+just run `./configure.py --bootstrap`; for more details see HACKING.md.
+(Also read that before making changes to Ninja, as it has advice.)
+
 Installation is not necessary because the only required file is is the
 resulting ninja binary. However, to enable features like Bash
 completion and Emacs and Vim editing modes, some files in misc/ must be
index 2eacbfe..7413659 100755 (executable)
@@ -28,7 +28,8 @@ import string
 import subprocess
 import sys
 
-sys.path.insert(0, 'misc')
+sourcedir = os.path.dirname(os.path.realpath(__file__))
+sys.path.insert(0, os.path.join(sourcedir, 'misc'))
 import ninja_syntax
 
 
@@ -155,12 +156,16 @@ class Bootstrap:
     def _expand_paths(self, paths):
         """Expand $vars in an array of paths, e.g. from a 'build' block."""
         paths = ninja_syntax.as_list(paths)
-        return ' '.join(map(self._expand, paths))
+        return ' '.join(map(self._shell_escape, (map(self._expand, paths))))
 
     def _expand(self, str, local_vars={}):
         """Expand $vars in a string."""
         return ninja_syntax.expand(str, self.vars, local_vars)
 
+    def _shell_escape(self, path):
+        """Quote paths containing spaces."""
+        return '"%s"' % path if ' ' in path else path
+
     def _run_command(self, cmdline):
         """Run a subcommand, quietly.  Prints the full command on error."""
         try:
@@ -251,11 +256,11 @@ if platform.is_msvc():
     objext = '.obj'
 
 def src(filename):
-    return os.path.join('src', filename)
+    return os.path.join('$sourcedir', 'src', filename)
 def built(filename):
     return os.path.join('$builddir', filename)
 def doc(filename):
-    return os.path.join('doc', filename)
+    return os.path.join('$sourcedir', 'doc', filename)
 def cc(name, **kwargs):
     return n.build(built(name + objext), 'cxx', src(name + '.c'), **kwargs)
 def cxx(name, **kwargs):
@@ -267,6 +272,7 @@ def binary(name):
         return exe
     return name
 
+n.variable('sourcedir', sourcedir)
 n.variable('builddir', 'build')
 n.variable('cxx', CXX)
 if platform.is_msvc():
@@ -353,6 +359,9 @@ if platform.supports_ppoll() and not options.force_pselect:
 if platform.supports_ninja_browse():
     cflags.append('-DNINJA_HAVE_BROWSE')
 
+# Search for generated headers relative to build dir.
+cflags.append('-I.')
+
 def shell_escape(str):
     """Escape str such that it's interpreted as a single argument by
     the shell."""
@@ -415,10 +424,10 @@ objs = []
 if platform.supports_ninja_browse():
     n.comment('browse_py.h is used to inline browse.py.')
     n.rule('inline',
-           command='src/inline.sh $varname < $in > $out',
+           command='"%s"' % src('inline.sh') + ' $varname < $in > $out',
            description='INLINE $out')
     n.build(built('browse_py.h'), 'inline', src('browse.py'),
-            implicit='src/inline.sh',
+            implicit=src('inline.sh'),
             variables=[('varname', 'kBrowsePy')])
     n.newline()
 
@@ -591,11 +600,12 @@ n.newline()
 if not host.is_mingw():
     n.comment('Regenerate build files if build script changes.')
     n.rule('configure',
-           command='${configure_env}%s configure.py $configure_args' %
+           command='${configure_env}%s $sourcedir/configure.py $configure_args' %
                options.with_python,
            generator=True)
     n.build('build.ninja', 'configure',
-            implicit=['configure.py', os.path.normpath('misc/ninja_syntax.py')])
+            implicit=['$sourcedir/configure.py',
+                      os.path.normpath('$sourcedir/misc/ninja_syntax.py')])
     n.newline()
 
 n.default(ninja)
index 217ff28..003c71e 100644 (file)
@@ -920,9 +920,12 @@ Evaluation and scoping
 
 Top-level variable declarations are scoped to the file they occur in.
 
+Rule declarations are also scoped to the file they occur in.
+_(Available since Ninja 1.6)_
+
 The `subninja` keyword, used to include another `.ninja` file,
 introduces a new scope.  The included `subninja` file may use the
-variables from the parent file, and shadow their values for the file's
+variables and rules from the parent file, and shadow their values for the file's
 scope, but it won't affect values of the variables in the parent.
 
 To include another `.ninja` file in the current scope, much like a C
index 83bfe43..8673919 100644 (file)
@@ -18,7 +18,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-#include "../build/browse_py.h"
+#include "build/browse_py.h"
 
 void RunBrowsePython(State* state, const char* ninja_command,
                      const char* initial_target) {
index fcfeba0..355285c 100644 (file)
@@ -55,6 +55,7 @@ bool DependencyScan::RecomputeDirty(Edge* edge, string* err) {
     if (!err->empty())
       return false;
     // Failed to load dependency info: rebuild to regenerate it.
+    // LoadDeps() did EXPLAIN() already, no need to do it here.
     dirty = edge->deps_missing_ = true;
   }
 
@@ -142,7 +143,12 @@ bool DependencyScan::RecomputeOutputDirty(Edge* edge,
   if (edge->is_phony()) {
     // Phony edges don't write any output.  Outputs are only dirty if
     // there are no inputs and we're missing the output.
-    return edge->inputs_.empty() && !output->exists();
+    if (edge->inputs_.empty() && !output->exists()) {
+      EXPLAIN("output %s of phony edge with no inputs doesn't exist",
+              output->path().c_str());
+      return true;
+    }
+    return false;
   }
 
   BuildLog::LogEntry* entry = 0;
index 9526712..5f8d41a 100644 (file)
@@ -122,7 +122,8 @@ private:
 
 /// An edge in the dependency graph; links between Nodes using Rules.
 struct Edge {
-  Edge() : rule_(NULL), env_(NULL), outputs_ready_(false), deps_missing_(false),
+  Edge() : rule_(NULL), pool_(NULL), env_(NULL),
+           outputs_ready_(false), deps_missing_(false),
            implicit_deps_(0), order_only_deps_(0) {}
 
   /// Return true if all inputs' in-edges are ready.
index f2d6711..4c2aea1 100644 (file)
@@ -18,7 +18,7 @@
 
 #include "util.h"
 
-const char* kNinjaVersion = "1.5.3.git";
+const char* kNinjaVersion = "1.6.0.git";
 
 void ParseVersion(const string& version, int* major, int* minor) {
   size_t end = version.find('.');