Merge pull request #1484 from smeenai/gyp-to-gn
Change gyp to gn
diff --git a/.gitignore b/.gitignore
index a86205b..11150c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,6 @@
# Ninja output
.ninja_deps
.ninja_log
+
+# Visual Studio Code project files
+/.vscode/
diff --git a/.travis.yml b/.travis.yml
index 093139b..19a9b28 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,15 @@
+matrix:
+ include:
+ - os: linux
+ compiler: gcc
+ - os: linux
+ compiler: clang
+ - os: osx
sudo: false
language: cpp
-compiler:
- - gcc
- - clang
-script: ./configure.py --bootstrap && ./ninja all && ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots && ./misc/ninja_syntax_test.py
+script:
+ - ./configure.py --bootstrap
+ - ./ninja all
+ - ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots
+ - ./misc/ninja_syntax_test.py
+ - ./misc/output_test.py
diff --git a/HACKING.md b/HACKING.md
index 9198bdf..086940b 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -19,8 +19,44 @@
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 of Visual Studio.
+running `vcvarsall` in a normal command prompt).
+
+For other combinations such as gcc/clang you will need the compiler
+(gcc/cl) in your PATH and you will have to set the appropriate
+platform configuration script.
+
+See below if you want to use mingw or some other compiler instead of
+Visual Studio.
+
+##### Using Visual Studio
+Assuming that you now have Python installed, then the steps for building under
+Windows using Visual Studio are:
+
+Clone and checkout the latest release (or whatever branch you want). You
+can do this in either a command prompt or by opening a git bash prompt:
+
+```
+ $ git clone git://github.com/ninja-build/ninja.git && cd ninja
+ $ git checkout release
+```
+
+Then:
+
+1. Open a Windows command prompt in the folder where you checked out ninja.
+2. Select the Microsoft build environment by running
+`vcvarsall.bat` with the appropriate environment.
+3. Build ninja and test it.
+
+The steps for a Visual Studio 2015 64-bit build are outlined here:
+
+```
+ > "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
+ > python configure.py --bootstrap
+ > ninja --help
+```
+Copy the ninja executable to another location, if desired, e.g. C:\local\Ninja.
+
+Finally add the path where ninja.exe is to the PATH variable.
### Adjusting build flags
@@ -95,7 +131,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
index c8e1a9d..4c64f29 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,14 +1,40 @@
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
+environment:
+ CLICOLOR_FORCE: 1
+ CHERE_INVOKING: 1 # Tell Bash to inherit the current working directory
+ matrix:
+ - MSYSTEM: MINGW64
+ - MSYSTEM: MSVC
- ninja.bootstrap.exe all
+for:
+ -
+ matrix:
+ only:
+ - MSYSTEM: MINGW64
+ build_script:
+ ps: "C:\\msys64\\usr\\bin\\bash -lc @\"\n
+ pacman -S --quiet --noconfirm --needed re2c 2>&1\n
+ sed -i 's|cmd /c $ar cqs $out.tmp $in && move /Y $out.tmp $out|$ar crs $out $in|g' configure.py\n
+ ./configure.py --bootstrap --platform mingw 2>&1\n
+ ./ninja all\n
+ ./ninja_test 2>&1\n
+ ./misc/ninja_syntax_test.py 2>&1\n\"@"
+ -
+ matrix:
+ only:
+ - MSYSTEM: MSVC
+ build_script:
+ - cmd: >-
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
- ninja_test
+ python configure.py --bootstrap
- python misc/ninja_syntax_test.py
+ ninja.bootstrap.exe all
+
+ ninja_test
+
+ python misc/ninja_syntax_test.py
+
test: off
diff --git a/configure.py b/configure.py
index 9e23a5a..78cd1de 100755
--- a/configure.py
+++ b/configure.py
@@ -98,7 +98,7 @@
return self._platform == 'aix'
def uses_usr_local(self):
- return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly')
+ return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly', 'netbsd')
def supports_ppoll(self):
return self._platform in ('freebsd', 'linux', 'openbsd', 'bitrig',
@@ -356,6 +356,11 @@
if platform.uses_usr_local():
cflags.append('-I/usr/local/include')
ldflags.append('-L/usr/local/lib')
+ if platform.is_aix():
+ # printf formats for int64_t, uint64_t; large file support
+ cflags.append('-D__STDC_FORMAT_MACROS')
+ cflags.append('-D_LARGE_FILES')
+
libs = []
@@ -409,7 +414,7 @@
if platform.is_msvc():
n.rule('cxx',
- command='$cxx $cflags -c $in /Fo$out',
+ command='$cxx $cflags -c $in /Fo$out /Fd' + built('$pdb'),
description='CXX $out',
deps='msvc' # /showIncludes is included in $cflags.
)
@@ -480,6 +485,9 @@
n.newline()
n.comment('Core source files all build into ninja library.')
+cxxvariables = []
+if platform.is_msvc():
+ cxxvariables = [('pdb', 'ninja.pdb')]
for name in ['build',
'build_log',
'clean',
@@ -500,15 +508,15 @@
'string_piece_util',
'util',
'version']:
- objs += cxx(name)
+ objs += cxx(name, variables=cxxvariables)
if platform.is_windows():
for name in ['subprocess-win32',
'includes_normalize-win32',
'msvc_helper-win32',
'msvc_helper_main-win32']:
- objs += cxx(name)
+ objs += cxx(name, variables=cxxvariables)
if platform.is_msvc():
- objs += cxx('minidump-win32')
+ objs += cxx('minidump-win32', variables=cxxvariables)
objs += cc('getopt')
else:
objs += cxx('subprocess-posix')
@@ -531,7 +539,7 @@
all_targets = []
n.comment('Main executable is library plus main() function.')
-objs = cxx('ninja')
+objs = cxx('ninja', variables=cxxvariables)
ninja = n.build(binary('ninja'), 'link', objs, implicit=ninja_lib,
variables=[('libs', libs)])
n.newline()
@@ -546,6 +554,8 @@
n.comment('Tests all build into ninja_test executable.')
objs = []
+if platform.is_msvc():
+ cxxvariables = [('pdb', 'ninja_test.pdb')]
for name in ['build_log_test',
'build_test',
@@ -564,10 +574,10 @@
'subprocess_test',
'test',
'util_test']:
- objs += cxx(name)
+ objs += cxx(name, variables=cxxvariables)
if platform.is_windows():
for name in ['includes_normalize_test', 'msvc_helper_test']:
- objs += cxx(name)
+ objs += cxx(name, variables=cxxvariables)
ninja_test = n.build(binary('ninja_test'), 'link', objs, implicit=ninja_lib,
variables=[('libs', libs)])
@@ -583,7 +593,9 @@
'hash_collision_bench',
'manifest_parser_perftest',
'clparser_perftest']:
- objs = cxx(name)
+ if platform.is_msvc():
+ cxxvariables = [('pdb', name + '.pdb')]
+ objs = cxx(name, variables=cxxvariables)
all_targets += n.build(binary(name), 'link', objs,
implicit=ninja_lib, variables=[('libs', libs)])
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 25101b3..d625fa9 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -593,7 +593,7 @@
to its stdout. Ninja then filters these lines from the displayed
output. No `depfile` attribute is necessary, but the localized string
in front of the the header file path. For instance
- `msvc_deps_prefix = Note: including file: `
+ `msvc_deps_prefix = Note: including file:`
for a English Visual Studio (the default). Should be globally defined.
+
----
@@ -880,7 +880,8 @@
are usually provided by the C library. If you need shell
interpretation of the command (such as the use of `&&` to chain
multiple commands), make the command execute the Windows shell by
-prefixing the command with `cmd /c`.
+prefixing the command with `cmd /c`. Ninja may error with "invalid parameter"
+which usually indicates that the command line length has been exceeded.
[[ref_outputs]]
Build outputs
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_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/misc/output_test.py b/misc/output_test.py
new file mode 100755
index 0000000..1dcde10
--- /dev/null
+++ b/misc/output_test.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+
+"""Runs ./ninja and checks if the output is correct.
+
+In order to simulate a smart terminal it uses the 'script' command.
+"""
+
+import os
+import platform
+import subprocess
+import sys
+import tempfile
+import unittest
+
+default_env = dict(os.environ)
+if 'NINJA_STATUS' in default_env:
+ del default_env['NINJA_STATUS']
+if 'CLICOLOR_FORCE' in default_env:
+ del default_env['CLICOLOR_FORCE']
+default_env['TERM'] = ''
+
+def run(build_ninja, flags='', pipe=False, env=default_env):
+ with tempfile.NamedTemporaryFile('w') as f:
+ f.write(build_ninja)
+ f.flush()
+ ninja_cmd = './ninja {} -f {}'.format(flags, f.name)
+ try:
+ if pipe:
+ output = subprocess.check_output([ninja_cmd], shell=True, env=env)
+ elif platform.system() == 'Darwin':
+ output = subprocess.check_output(['script', '-q', '/dev/null', 'bash', '-c', ninja_cmd],
+ env=env)
+ else:
+ output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'],
+ env=env)
+ except subprocess.CalledProcessError as err:
+ sys.stdout.buffer.write(err.output)
+ raise err
+ final_output = ''
+ for line in output.decode('utf-8').splitlines(True):
+ if len(line) > 0 and line[-1] == '\r':
+ continue
+ final_output += line.replace('\r', '')
+ return final_output
+
+class Output(unittest.TestCase):
+ def test_issue_1418(self):
+ self.assertEqual(run(
+'''rule echo
+ command = sleep $delay && echo $out
+ description = echo $out
+
+build a: echo
+ delay = 3
+build b: echo
+ delay = 2
+build c: echo
+ delay = 1
+'''),
+'''[1/3] echo c\x1b[K
+c
+[2/3] echo b\x1b[K
+b
+[3/3] echo a\x1b[K
+a
+''')
+
+ def test_issue_1214(self):
+ print_red = '''rule echo
+ command = printf '\x1b[31mred\x1b[0m'
+ description = echo $out
+
+build a: echo
+'''
+ # Only strip color when ninja's output is piped.
+ self.assertEqual(run(print_red),
+'''[1/1] echo a\x1b[K
+\x1b[31mred\x1b[0m
+''')
+ self.assertEqual(run(print_red, pipe=True),
+'''[1/1] echo a
+red
+''')
+ # Even in verbose mode, colors should still only be stripped when piped.
+ self.assertEqual(run(print_red, flags='-v'),
+'''[1/1] printf '\x1b[31mred\x1b[0m'
+\x1b[31mred\x1b[0m
+''')
+ self.assertEqual(run(print_red, flags='-v', pipe=True),
+'''[1/1] printf '\x1b[31mred\x1b[0m'
+red
+''')
+
+ # CLICOLOR_FORCE=1 can be used to disable escape code stripping.
+ env = default_env.copy()
+ env['CLICOLOR_FORCE'] = '1'
+ self.assertEqual(run(print_red, pipe=True, env=env),
+'''[1/1] echo a
+\x1b[31mred\x1b[0m
+''')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/misc/zsh-completion b/misc/zsh-completion
index bf23fac..4cee3b8 100644
--- a/misc/zsh-completion
+++ b/misc/zsh-completion
@@ -14,7 +14,7 @@
# limitations under the License.
# Add the following to your .zshrc to tab-complete ninja targets
-# . path/to/ninja/misc/zsh-completion
+# fpath=(path/to/ninja/misc/zsh-completion $fpath)
__get_targets() {
dir="."
diff --git a/src/browse.cc b/src/browse.cc
index 14900f8..c08c9f4 100644
--- a/src/browse.cc
+++ b/src/browse.cc
@@ -14,6 +14,7 @@
#include "browse.h"
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -57,7 +58,11 @@
}
command.push_back(NULL);
execvp(command[0], (char**)&command[0]);
- perror("ninja: execvp");
+ if (errno == ENOENT) {
+ printf("ninja: %s is required for the browse tool\n", NINJA_PYTHON);
+ } else {
+ perror("ninja: execvp");
+ }
} while (false);
_exit(1);
} else { // Child.
diff --git a/src/browse.py b/src/browse.py
index 64a16f2..1c9c39b 100755
--- a/src/browse.py
+++ b/src/browse.py
@@ -24,8 +24,10 @@
try:
import http.server as httpserver
+ import socketserver
except ImportError:
import BaseHTTPServer as httpserver
+ import SocketServer as socketserver
import argparse
import cgi
import os
@@ -205,10 +207,14 @@
parser.add_argument('initial_target', default='all', nargs='?',
help='Initial target to show (default %(default)s)')
+class HTTPServer(socketserver.ThreadingMixIn, httpserver.HTTPServer):
+ # terminate server immediately when Python exits.
+ daemon_threads = True
+
args = parser.parse_args()
port = args.port
hostname = args.hostname
-httpd = httpserver.HTTPServer((hostname,port), RequestHandler)
+httpd = HTTPServer((hostname,port), RequestHandler)
try:
if hostname == "":
hostname = socket.gethostname()
diff --git a/src/build.cc b/src/build.cc
index c24d6a9..b392803 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -16,7 +16,6 @@
#include <assert.h>
#include <errno.h>
-#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <functional>
@@ -131,18 +130,6 @@
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) {
@@ -167,9 +154,8 @@
// (Launching subprocesses in pseudo ttys doesn't work because there are
// only a few hundred available on some systems, and ninja can launch
// thousands of parallel compile commands.)
- // TODO: There should be a flag to disable escape code stripping.
string final_output;
- if (!printer_.is_smart_terminal())
+ if (!printer_.supports_color())
final_output = StripAnsiEscapeCodes(output);
else
final_output = output;
@@ -571,7 +557,8 @@
BuildLog* build_log, DepsLog* deps_log,
DiskInterface* disk_interface)
: state_(state), config_(config), disk_interface_(disk_interface),
- scan_(state, build_log, deps_log, disk_interface) {
+ scan_(state, build_log, deps_log, disk_interface,
+ &config_.depfile_parser_options) {
status_ = new BuildStatus(config);
}
@@ -920,7 +907,7 @@
if (content.empty())
return true;
- DepfileParser deps;
+ DepfileParser deps(config_.depfile_parser_options);
if (!deps.Parse(&content, err))
return false;
diff --git a/src/build.h b/src/build.h
index ac7f951..a42b8d4 100644
--- a/src/build.h
+++ b/src/build.h
@@ -23,6 +23,7 @@
#include <string>
#include <vector>
+#include "depfile_parser.h"
#include "graph.h" // XXX needed for DependencyScan; should rearrange.
#include "exit_status.h"
#include "line_printer.h"
@@ -151,6 +152,7 @@
/// The maximum load average we must not exceed. A negative value
/// means that we do not have any limit.
double max_load_average;
+ DepfileParserOptions depfile_parser_options;
};
/// Builder wraps the build process: starting commands, updating status.
@@ -222,7 +224,6 @@
enum EdgeStatus {
kEdgeStarted,
- kEdgeRunning,
kEdgeFinished,
};
diff --git a/src/build_log.cc b/src/build_log.cc
index c75be95..c4a08a0 100644
--- a/src/build_log.cc
+++ b/src/build_log.cc
@@ -35,6 +35,9 @@
#include "graph.h"
#include "metrics.h"
#include "util.h"
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+#define strtoll _strtoi64
+#endif
// Implementation details:
// Each run's log appends to the log file.
@@ -76,11 +79,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;
};
diff --git a/src/clean.cc b/src/clean.cc
index 4f31a03..ce6a575 100644
--- a/src/clean.cc
+++ b/src/clean.cc
@@ -101,6 +101,7 @@
printf("\n");
else
printf(" ");
+ fflush(stdout);
}
void Cleaner::PrintFooter() {
diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc
index 7cee892..405289f 100644
--- a/src/depfile_parser.cc
+++ b/src/depfile_parser.cc
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.13.5 */
+/* Generated by re2c 1.1.1 */
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,12 @@
// limitations under the License.
#include "depfile_parser.h"
+#include "util.h"
+
+DepfileParser::DepfileParser(DepfileParserOptions options)
+ : options_(options)
+{
+}
// A note on backslashes in Makefiles, from reading the docs:
// Backslash-newline is the line continuation character.
@@ -35,8 +41,13 @@
// parsing_targets: whether we are parsing targets or dependencies.
char* in = &(*content)[0];
char* end = in + content->size();
+ bool have_target = false;
+ bool have_secondary_target_on_this_rule = false;
+ bool have_newline_since_primary_target = false;
+ bool warned_distinct_target_lines = false;
bool parsing_targets = true;
while (in < end) {
+ bool have_newline = false;
// out: current output point (typically same as in, but can fall behind
// as we de-escape backslashes).
char* out = in;
@@ -45,6 +56,7 @@
for (;;) {
// start: beginning of the current parsed span.
const char* start = in;
+ char* yymarker = NULL;
{
unsigned char yych;
@@ -53,7 +65,7 @@
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
- 0, 128, 0, 0, 0, 0, 0, 0,
+ 0, 128, 0, 0, 0, 128, 0, 0,
128, 128, 0, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 0, 0, 128, 0, 0,
@@ -82,88 +94,55 @@
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
};
-
yych = *in;
- if (yych <= '=') {
+ if (yybm[0+yych] & 128) {
+ goto yy9;
+ }
+ if (yych <= '\r') {
+ if (yych <= '\t') {
+ if (yych >= 0x01) goto yy4;
+ } else {
+ if (yych <= '\n') goto yy6;
+ if (yych <= '\f') goto yy4;
+ goto yy8;
+ }
+ } else {
if (yych <= '$') {
- if (yych <= ' ') {
- if (yych <= 0x00) goto yy7;
- goto yy9;
- } else {
- if (yych <= '!') goto yy5;
- if (yych <= '#') goto yy9;
- goto yy4;
- }
+ if (yych <= '#') goto yy4;
+ goto yy12;
} else {
- if (yych <= '*') {
- if (yych <= '\'') goto yy9;
- if (yych <= ')') goto yy5;
- goto yy9;
- } else {
- if (yych <= ':') goto yy5;
- if (yych <= '<') goto yy9;
- goto yy5;
- }
- }
- } else {
- if (yych <= '_') {
- if (yych <= '[') {
- if (yych <= '?') goto yy9;
- if (yych <= 'Z') goto yy5;
- goto yy9;
- } else {
- if (yych <= '\\') goto yy2;
- if (yych <= '^') goto yy9;
- goto yy5;
- }
- } else {
- if (yych <= '|') {
- if (yych <= '`') goto yy9;
- if (yych <= '{') goto yy5;
- goto yy9;
- } else {
- if (yych == 0x7F) goto yy9;
- goto yy5;
- }
+ if (yych == '\\') goto yy13;
+ goto yy4;
}
}
-yy2:
++in;
- if ((yych = *in) <= '"') {
- if (yych <= '\f') {
- if (yych <= 0x00) goto yy3;
- if (yych != '\n') goto yy14;
- } else {
- if (yych <= '\r') goto yy3;
- if (yych == ' ') goto yy16;
- goto yy14;
- }
- } else {
- if (yych <= 'Z') {
- if (yych <= '#') goto yy16;
- if (yych == '*') goto yy16;
- goto yy14;
- } else {
- if (yych <= '\\') goto yy16;
- if (yych == '|') goto yy16;
- goto yy14;
- }
+ {
+ break;
}
-yy3:
+yy4:
+ ++in;
+yy5:
{
// For any other character (e.g. whitespace), swallow it here,
// allowing the outer logic to loop around again.
break;
}
-yy4:
- yych = *++in;
- if (yych == '$') goto yy12;
- goto yy3;
-yy5:
- ++in;
- yych = *in;
- goto yy11;
yy6:
+ ++in;
+ {
+ // A newline ends the current file name and the current rule.
+ have_newline = true;
+ break;
+ }
+yy8:
+ yych = *++in;
+ if (yych == '\n') goto yy6;
+ goto yy5;
+yy9:
+ yych = *++in;
+ if (yybm[0+yych] & 128) {
+ goto yy9;
+ }
{
// Got a span of plain text.
int len = (int)(in - start);
@@ -173,30 +152,41 @@
out += len;
continue;
}
-yy7:
- ++in;
- {
- break;
- }
-yy9:
- yych = *++in;
- goto yy3;
-yy10:
- ++in;
- yych = *in;
-yy11:
- if (yybm[0+yych] & 128) {
- goto yy10;
- }
- goto yy6;
yy12:
+ yych = *++in;
+ if (yych == '$') goto yy14;
+ goto yy5;
+yy13:
+ yych = *(yymarker = ++in);
+ if (yych <= '"') {
+ if (yych <= '\f') {
+ if (yych <= 0x00) goto yy5;
+ if (yych == '\n') goto yy18;
+ goto yy16;
+ } else {
+ if (yych <= '\r') goto yy20;
+ if (yych == ' ') goto yy22;
+ goto yy16;
+ }
+ } else {
+ if (yych <= 'Z') {
+ if (yych <= '#') goto yy22;
+ if (yych == '*') goto yy22;
+ goto yy16;
+ } else {
+ if (yych <= ']') goto yy22;
+ if (yych == '|') goto yy22;
+ goto yy16;
+ }
+ }
+yy14:
++in;
{
// De-escape dollar character.
*out++ = '$';
continue;
}
-yy14:
+yy16:
++in;
{
// Let backslash before other characters through verbatim.
@@ -204,7 +194,18 @@
*out++ = yych;
continue;
}
-yy16:
+yy18:
+ ++in;
+ {
+ // A line continuation ends the current file name.
+ break;
+ }
+yy20:
+ yych = *++in;
+ if (yych == '\n') goto yy18;
+ in = yymarker;
+ goto yy5;
+yy22:
++in;
{
// De-escape backslashed character.
@@ -216,25 +217,52 @@
}
int len = (int)(out - filename);
- const bool is_target = parsing_targets;
+ const bool is_dependency = !parsing_targets;
if (len > 0 && filename[len - 1] == ':') {
len--; // Strip off trailing colon, if any.
parsing_targets = false;
+ have_target = true;
}
- if (len == 0)
- continue;
+ if (len > 0) {
+ if (is_dependency) {
+ if (have_secondary_target_on_this_rule) {
+ if (!have_newline_since_primary_target) {
+ *err = "depfile has multiple output paths";
+ return false;
+ } else if (options_.depfile_distinct_target_lines_action_ ==
+ kDepfileDistinctTargetLinesActionError) {
+ *err =
+ "depfile has multiple output paths (on separate lines)"
+ " [-w depfilemulti=err]";
+ return false;
+ } else {
+ if (!warned_distinct_target_lines) {
+ warned_distinct_target_lines = true;
+ Warning("depfile has multiple output paths (on separate lines); "
+ "continuing anyway [-w depfilemulti=warn]");
+ }
+ continue;
+ }
+ }
+ ins_.push_back(StringPiece(filename, len));
+ } else if (!out_.str_) {
+ out_ = StringPiece(filename, len);
+ } else if (out_ != StringPiece(filename, len)) {
+ have_secondary_target_on_this_rule = true;
+ }
+ }
- if (!is_target) {
- ins_.push_back(StringPiece(filename, len));
- } else if (!out_.str_) {
- out_ = StringPiece(filename, len);
- } else if (out_ != StringPiece(filename, len)) {
- *err = "depfile has multiple output paths";
- return false;
+ if (have_newline) {
+ // A newline ends a rule so the next filename will be a new target.
+ parsing_targets = true;
+ have_secondary_target_on_this_rule = false;
+ if (have_target) {
+ have_newline_since_primary_target = true;
+ }
}
}
- if (parsing_targets) {
+ if (!have_target) {
*err = "expected ':' in depfile";
return false;
}
diff --git a/src/depfile_parser.h b/src/depfile_parser.h
index 1e6ebb5..be20374 100644
--- a/src/depfile_parser.h
+++ b/src/depfile_parser.h
@@ -21,8 +21,24 @@
#include "string_piece.h"
+enum DepfileDistinctTargetLinesAction {
+ kDepfileDistinctTargetLinesActionWarn,
+ kDepfileDistinctTargetLinesActionError,
+};
+
+struct DepfileParserOptions {
+ DepfileParserOptions()
+ : depfile_distinct_target_lines_action_(
+ kDepfileDistinctTargetLinesActionWarn) {}
+ DepfileDistinctTargetLinesAction
+ depfile_distinct_target_lines_action_;
+};
+
/// Parser for the dependency information emitted by gcc's -M flags.
struct DepfileParser {
+ explicit DepfileParser(DepfileParserOptions options =
+ DepfileParserOptions());
+
/// Parse an input file. Input must be NUL-terminated.
/// Warning: may mutate the content in-place and parsed StringPieces are
/// pointers within it.
@@ -30,6 +46,7 @@
StringPiece out_;
vector<StringPiece> ins_;
+ DepfileParserOptions options_;
};
#endif // NINJA_DEPFILE_PARSER_H_
diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc
index 98c1621..f8c94b3 100644
--- a/src/depfile_parser.in.cc
+++ b/src/depfile_parser.in.cc
@@ -13,6 +13,12 @@
// limitations under the License.
#include "depfile_parser.h"
+#include "util.h"
+
+DepfileParser::DepfileParser(DepfileParserOptions options)
+ : options_(options)
+{
+}
// A note on backslashes in Makefiles, from reading the docs:
// Backslash-newline is the line continuation character.
@@ -34,8 +40,13 @@
// parsing_targets: whether we are parsing targets or dependencies.
char* in = &(*content)[0];
char* end = in + content->size();
+ bool have_target = false;
+ bool have_secondary_target_on_this_rule = false;
+ bool have_newline_since_primary_target = false;
+ bool warned_distinct_target_lines = false;
bool parsing_targets = true;
while (in < end) {
+ bool have_newline = false;
// out: current output point (typically same as in, but can fall behind
// as we de-escape backslashes).
char* out = in;
@@ -44,10 +55,12 @@
for (;;) {
// start: beginning of the current parsed span.
const char* start = in;
+ char* yymarker = NULL;
/*!re2c
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYCURSOR = in;
re2c:define:YYLIMIT = end;
+ re2c:define:YYMARKER = yymarker;
re2c:yyfill:enable = 0;
@@ -55,7 +68,8 @@
re2c:indent:string = " ";
nul = "\000";
- escape = [ \\#*[|];
+ escape = [ \\#*[|\]];
+ newline = '\r'?'\n';
'\\' escape {
// De-escape backslashed character.
@@ -73,7 +87,7 @@
*out++ = yych;
continue;
}
- [a-zA-Z0-9+,/_:.~()}{@=!\x80-\xFF-]+ {
+ [a-zA-Z0-9+,/_:.~()}{%@=!\x80-\xFF-]+ {
// Got a span of plain text.
int len = (int)(in - start);
// Need to shift it over if we're overwriting backslashes.
@@ -85,6 +99,15 @@
nul {
break;
}
+ '\\' newline {
+ // A line continuation ends the current file name.
+ break;
+ }
+ newline {
+ // A newline ends the current file name and the current rule.
+ have_newline = true;
+ break;
+ }
[^] {
// For any other character (e.g. whitespace), swallow it here,
// allowing the outer logic to loop around again.
@@ -94,25 +117,52 @@
}
int len = (int)(out - filename);
- const bool is_target = parsing_targets;
+ const bool is_dependency = !parsing_targets;
if (len > 0 && filename[len - 1] == ':') {
len--; // Strip off trailing colon, if any.
parsing_targets = false;
+ have_target = true;
}
- if (len == 0)
- continue;
+ if (len > 0) {
+ if (is_dependency) {
+ if (have_secondary_target_on_this_rule) {
+ if (!have_newline_since_primary_target) {
+ *err = "depfile has multiple output paths";
+ return false;
+ } else if (options_.depfile_distinct_target_lines_action_ ==
+ kDepfileDistinctTargetLinesActionError) {
+ *err =
+ "depfile has multiple output paths (on separate lines)"
+ " [-w depfilemulti=err]";
+ return false;
+ } else {
+ if (!warned_distinct_target_lines) {
+ warned_distinct_target_lines = true;
+ Warning("depfile has multiple output paths (on separate lines); "
+ "continuing anyway [-w depfilemulti=warn]");
+ }
+ continue;
+ }
+ }
+ ins_.push_back(StringPiece(filename, len));
+ } else if (!out_.str_) {
+ out_ = StringPiece(filename, len);
+ } else if (out_ != StringPiece(filename, len)) {
+ have_secondary_target_on_this_rule = true;
+ }
+ }
- if (!is_target) {
- ins_.push_back(StringPiece(filename, len));
- } else if (!out_.str_) {
- out_ = StringPiece(filename, len);
- } else if (out_ != StringPiece(filename, len)) {
- *err = "depfile has multiple output paths";
- return false;
+ if (have_newline) {
+ // A newline ends a rule so the next filename will be a new target.
+ parsing_targets = true;
+ have_secondary_target_on_this_rule = false;
+ if (have_target) {
+ have_newline_since_primary_target = true;
+ }
}
}
- if (parsing_targets) {
+ if (!have_target) {
*err = "expected ':' in depfile";
return false;
}
diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc
index ee798f8..52fe7cd 100644
--- a/src/depfile_parser_test.cc
+++ b/src/depfile_parser_test.cc
@@ -119,15 +119,16 @@
// https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/
string err;
EXPECT_TRUE(Parse(
-"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n"
-" en@quot.header~ t+t-x!=1 \n"
-" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\n"
-" Fu\303\244ball",
+"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \\\n"
+" en@quot.header~ t+t-x!=1 \\\n"
+" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\\\n"
+" Fu\303\244ball\\\n"
+" a\\[1\\]b@2%c",
&err));
ASSERT_EQ("", err);
EXPECT_EQ("C:/Program Files (x86)/Microsoft crtdefs.h",
parser_.out_.AsString());
- ASSERT_EQ(4u, parser_.ins_.size());
+ ASSERT_EQ(5u, parser_.ins_.size());
EXPECT_EQ("en@quot.header~",
parser_.ins_[0].AsString());
EXPECT_EQ("t+t-x!=1",
@@ -136,6 +137,8 @@
parser_.ins_[2].AsString());
EXPECT_EQ("Fu\303\244ball",
parser_.ins_[3].AsString());
+ EXPECT_EQ("a[1]b@2%c",
+ parser_.ins_[4].AsString());
}
TEST_F(DepfileParserTest, UnifyMultipleOutputs) {
@@ -155,3 +158,133 @@
EXPECT_FALSE(Parse("foo bar: x y z", &err));
ASSERT_EQ("depfile has multiple output paths", err);
}
+
+TEST_F(DepfileParserTest, MultipleEmptyRules) {
+ string err;
+ EXPECT_TRUE(Parse("foo: x\n"
+ "foo: \n"
+ "foo:\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(1u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+}
+
+TEST_F(DepfileParserTest, UnifyMultipleRulesLF) {
+ string err;
+ EXPECT_TRUE(Parse("foo: x\n"
+ "foo: y\n"
+ "foo \\\n"
+ "foo: z\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, UnifyMultipleRulesCRLF) {
+ string err;
+ EXPECT_TRUE(Parse("foo: x\r\n"
+ "foo: y\r\n"
+ "foo \\\r\n"
+ "foo: z\r\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, UnifyMixedRulesLF) {
+ string err;
+ EXPECT_TRUE(Parse("foo: x\\\n"
+ " y\n"
+ "foo \\\n"
+ "foo: z\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, UnifyMixedRulesCRLF) {
+ string err;
+ EXPECT_TRUE(Parse("foo: x\\\r\n"
+ " y\r\n"
+ "foo \\\r\n"
+ "foo: z\r\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, IndentedRulesLF) {
+ string err;
+ EXPECT_TRUE(Parse(" foo: x\n"
+ " foo: y\n"
+ " foo: z\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, IndentedRulesCRLF) {
+ string err;
+ EXPECT_TRUE(Parse(" foo: x\r\n"
+ " foo: y\r\n"
+ " foo: z\r\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, TolerateMP) {
+ string err;
+ EXPECT_TRUE(Parse("foo: x y z\n"
+ "x:\n"
+ "y:\n"
+ "z:\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, MultipleRulesTolerateMP) {
+ string err;
+ EXPECT_TRUE(Parse("foo: x\n"
+ "x:\n"
+ "foo: y\n"
+ "y:\n"
+ "foo: z\n"
+ "z:\n", &err));
+ ASSERT_EQ("foo", parser_.out_.AsString());
+ ASSERT_EQ(3u, parser_.ins_.size());
+ EXPECT_EQ("x", parser_.ins_[0].AsString());
+ EXPECT_EQ("y", parser_.ins_[1].AsString());
+ EXPECT_EQ("z", parser_.ins_[2].AsString());
+}
+
+TEST_F(DepfileParserTest, MultipleRulesRejectDifferentOutputs) {
+ // check that multiple different outputs are rejected by the parser
+ // when spread across multiple rules
+ DepfileParserOptions parser_opts;
+ parser_opts.depfile_distinct_target_lines_action_ =
+ kDepfileDistinctTargetLinesActionError;
+ DepfileParser parser(parser_opts);
+ string err;
+ string input =
+ "foo: x y\n"
+ "bar: y z\n";
+ EXPECT_FALSE(parser.Parse(&input, &err));
+ ASSERT_EQ("depfile has multiple output paths (on separate lines)"
+ " [-w depfilemulti=err]", err);
+}
diff --git a/src/deps_log.cc b/src/deps_log.cc
index eb81a37..0bb96f3 100644
--- a/src/deps_log.cc
+++ b/src/deps_log.cc
@@ -20,6 +20,9 @@
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
+#elif defined(_MSC_VER) && (_MSC_VER < 1900)
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
#endif
#include "graph.h"
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 504c679..d4c2fb0 100644
--- a/src/disk_interface.cc
+++ b/src/disk_interface.cc
@@ -35,14 +35,15 @@
string DirName(const string& path) {
#ifdef _WIN32
- const char kPathSeparators[] = "\\/";
+ static const char kPathSeparators[] = "\\/";
#else
- const char kPathSeparators[] = "/";
+ static const char kPathSeparators[] = "/";
#endif
+ static const char* const kEnd = kPathSeparators + sizeof(kPathSeparators) - 1;
+
string::size_type slash_pos = path.find_last_of(kPathSeparators);
if (slash_pos == string::npos)
return string(); // Nothing to do.
- const char* const kEnd = kPathSeparators + strlen(kPathSeparators);
while (slash_pos > 0 &&
std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd)
--slash_pos;
@@ -205,13 +206,15 @@
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)))
+ defined(__BIONIC__) || (defined (__SVR4) && defined (__sun)) || defined(__FreeBSD__))
// 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;
+#elif defined(_AIX)
+ return (int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n;
#else
return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec;
#endif
diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc
index 81aa63a..bac515d 100644
--- a/src/disk_interface_test.cc
+++ b/src/disk_interface_test.cc
@@ -139,11 +139,13 @@
EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
EXPECT_EQ("", err);
+#ifndef _MSC_VER // TODO: Investigate why. Also see https://github.com/ninja-build/ninja/pull/1423
EXPECT_EQ(disk_.Stat("subdir", &err),
disk_.Stat("subdir/.", &err));
EXPECT_EQ("", err);
EXPECT_EQ(disk_.Stat("subdir", &err),
disk_.Stat("subdir/subsubdir/..", &err));
+#endif
EXPECT_EQ("", err);
EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached);
EXPECT_EQ("", err);
@@ -211,7 +213,7 @@
struct StatTest : public StateTestWithBuiltinRules,
public DiskInterface {
- StatTest() : scan_(&state_, NULL, NULL, this) {}
+ StatTest() : scan_(&state_, NULL, NULL, this, NULL) {}
// DiskInterface implementation.
virtual TimeStamp Stat(const string& path, string* err) const;
diff --git a/src/graph.cc b/src/graph.cc
index b41c247..9c2f784 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -491,7 +491,9 @@
return false;
}
- DepfileParser depfile;
+ DepfileParser depfile(depfile_parser_options_
+ ? *depfile_parser_options_
+ : DepfileParserOptions());
string depfile_err;
if (!depfile.Parse(&content, &depfile_err)) {
*err = path + ": " + depfile_err;
diff --git a/src/graph.h b/src/graph.h
index a8f0641..d58fecd 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -24,6 +24,7 @@
#include "util.h"
struct BuildLog;
+struct DepfileParserOptions;
struct DiskInterface;
struct DepsLog;
struct Edge;
@@ -209,8 +210,10 @@
/// "depfile" attribute in build files.
struct ImplicitDepLoader {
ImplicitDepLoader(State* state, DepsLog* deps_log,
- DiskInterface* disk_interface)
- : state_(state), disk_interface_(disk_interface), deps_log_(deps_log) {}
+ DiskInterface* disk_interface,
+ DepfileParserOptions const* depfile_parser_options)
+ : state_(state), disk_interface_(disk_interface), deps_log_(deps_log),
+ depfile_parser_options_(depfile_parser_options) {}
/// Load implicit dependencies for \a edge.
/// @return false on error (without filling \a err if info is just missing
@@ -242,6 +245,7 @@
State* state_;
DiskInterface* disk_interface_;
DepsLog* deps_log_;
+ DepfileParserOptions const* depfile_parser_options_;
};
@@ -249,10 +253,11 @@
/// and updating the dirty/outputs_ready state of all the nodes and edges.
struct DependencyScan {
DependencyScan(State* state, BuildLog* build_log, DepsLog* deps_log,
- DiskInterface* disk_interface)
+ DiskInterface* disk_interface,
+ DepfileParserOptions const* depfile_parser_options)
: build_log_(build_log),
disk_interface_(disk_interface),
- dep_loader_(state, deps_log, disk_interface) {}
+ dep_loader_(state, deps_log, disk_interface, depfile_parser_options) {}
/// Update the |dirty_| state of the given node by inspecting its input edge.
/// Examine inputs, outputs, and command lines to judge whether an edge
diff --git a/src/graph_test.cc b/src/graph_test.cc
index 422bc9a..4a66831 100644
--- a/src/graph_test.cc
+++ b/src/graph_test.cc
@@ -18,7 +18,7 @@
#include "test.h"
struct GraphTest : public StateTestWithBuiltinRules {
- GraphTest() : scan_(&state_, NULL, NULL, &fs_) {}
+ GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL) {}
VirtualFileSystem fs_;
DependencyScan scan_;
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 795542b..79bf5b4 100644
--- a/src/includes_normalize-win32.cc
+++ b/src/includes_normalize-win32.cc
@@ -26,6 +26,21 @@
namespace {
+bool InternalGetFullPathName(const StringPiece& file_name, char* buffer,
+ size_t buffer_length, string *err) {
+ DWORD result_size = GetFullPathNameA(file_name.AsString().c_str(),
+ buffer_length, buffer, NULL);
+ if (result_size == 0) {
+ *err = "GetFullPathNameA(" + file_name.AsString() + "): " +
+ GetLastErrorString();
+ return false;
+ } else if (result_size > buffer_length) {
+ *err = "path too long";
+ return false;
+ }
+ return true;
+}
+
bool IsPathSeparator(char c) {
return c == '/' || c == '\\';
}
@@ -54,15 +69,19 @@
}
// Return true if paths a and b are on the same Windows drive.
-bool SameDrive(StringPiece a, StringPiece b) {
+bool SameDrive(StringPiece a, StringPiece b, string* err) {
if (SameDriveFast(a, b)) {
return true;
}
char a_absolute[_MAX_PATH];
char b_absolute[_MAX_PATH];
- GetFullPathNameA(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL);
- GetFullPathNameA(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL);
+ if (!InternalGetFullPathName(a, a_absolute, sizeof(a_absolute), err)) {
+ return false;
+ }
+ if (!InternalGetFullPathName(b, b_absolute, sizeof(b_absolute), err)) {
+ return false;
+ }
char a_drive[_MAX_DIR];
char b_drive[_MAX_DIR];
_splitpath(a_absolute, a_drive, NULL, NULL, NULL);
@@ -106,11 +125,15 @@
} // anonymous namespace
IncludesNormalize::IncludesNormalize(const string& relative_to) {
- relative_to_ = AbsPath(relative_to);
+ string err;
+ relative_to_ = AbsPath(relative_to, &err);
+ if (!err.empty()) {
+ Fatal("Initializing IncludesNormalize(): %s", err.c_str());
+ }
split_relative_to_ = SplitStringPiece(relative_to_, '/');
}
-string IncludesNormalize::AbsPath(StringPiece s) {
+string IncludesNormalize::AbsPath(StringPiece s, string* err) {
if (IsFullPathName(s)) {
string result = s.AsString();
for (size_t i = 0; i < result.size(); ++i) {
@@ -122,7 +145,9 @@
}
char result[_MAX_PATH];
- GetFullPathNameA(s.AsString().c_str(), sizeof(result), result, NULL);
+ if (!InternalGetFullPathName(s, result, sizeof(result), err)) {
+ return "";
+ }
for (char* c = result; *c; ++c)
if (*c == '\\')
*c = '/';
@@ -130,8 +155,10 @@
}
string IncludesNormalize::Relativize(
- StringPiece path, const vector<StringPiece>& start_list) {
- string abs_path = AbsPath(path);
+ StringPiece path, const vector<StringPiece>& start_list, string* err) {
+ string abs_path = AbsPath(path, err);
+ if (!err->empty())
+ return "";
vector<StringPiece> path_list = SplitStringPiece(abs_path, '/');
int i;
for (i = 0; i < static_cast<int>(min(start_list.size(), path_list.size()));
@@ -165,12 +192,18 @@
if (!CanonicalizePath(copy, &len, &slash_bits, err))
return false;
StringPiece partially_fixed(copy, len);
- string abs_input = AbsPath(partially_fixed);
+ string abs_input = AbsPath(partially_fixed, err);
+ if (!err->empty())
+ return false;
- if (!SameDrive(abs_input, relative_to_)) {
+ if (!SameDrive(abs_input, relative_to_, err)) {
+ if (!err->empty())
+ return false;
*result = partially_fixed.AsString();
return true;
}
- *result = Relativize(abs_input, split_relative_to_);
+ *result = Relativize(abs_input, split_relative_to_, err);
+ if (!err->empty())
+ return false;
return true;
}
diff --git a/src/includes_normalize.h b/src/includes_normalize.h
index 3811e53..0339581 100644
--- a/src/includes_normalize.h
+++ b/src/includes_normalize.h
@@ -25,9 +25,9 @@
IncludesNormalize(const string& relative_to);
// Internal utilities made available for testing, maybe useful otherwise.
- static string AbsPath(StringPiece s);
+ static string AbsPath(StringPiece s, string* err);
static string Relativize(StringPiece path,
- const vector<StringPiece>& start_list);
+ const vector<StringPiece>& start_list, string* err);
/// Normalize by fixing slashes style, fixing redundant .. and . and makes the
/// path |input| relative to |this->relative_to_| and store to |result|.
diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc
index eac36fd..dbcdbe0 100644
--- a/src/includes_normalize_test.cc
+++ b/src/includes_normalize_test.cc
@@ -58,9 +58,12 @@
}
TEST(IncludesNormalize, WithRelative) {
+ string err;
string currentdir = GetCurDir();
EXPECT_EQ("c", NormalizeRelativeAndCheckNoError("a/b/c", "a/b"));
- EXPECT_EQ("a", NormalizeAndCheckNoError(IncludesNormalize::AbsPath("a")));
+ EXPECT_EQ("a",
+ NormalizeAndCheckNoError(IncludesNormalize::AbsPath("a", &err)));
+ EXPECT_EQ("", err);
EXPECT_EQ(string("../") + currentdir + string("/a"),
NormalizeRelativeAndCheckNoError("a", "../b"));
EXPECT_EQ(string("../") + currentdir + string("/a/b"),
@@ -138,3 +141,27 @@
EXPECT_EQ(forward_slashes.substr(cwd_len + 1),
NormalizeAndCheckNoError(kExactlyMaxPath));
}
+
+TEST(IncludesNormalize, ShortRelativeButTooLongAbsolutePath) {
+ string result, err;
+ IncludesNormalize normalizer(".");
+ // A short path should work
+ EXPECT_TRUE(normalizer.Normalize("a", &result, &err));
+ EXPECT_EQ("", err);
+
+ // Construct max size path having cwd prefix.
+ // kExactlyMaxPath = "aaaa\\aaaa...aaaa\0";
+ char kExactlyMaxPath[_MAX_PATH + 1];
+ for (int i = 0; i < _MAX_PATH; ++i) {
+ if (i < _MAX_PATH - 1 && i % 10 == 4)
+ kExactlyMaxPath[i] = '\\';
+ else
+ kExactlyMaxPath[i] = 'a';
+ }
+ kExactlyMaxPath[_MAX_PATH] = '\0';
+ EXPECT_EQ(strlen(kExactlyMaxPath), _MAX_PATH);
+
+ // Make sure a path that's exactly _MAX_PATH long fails with a proper error.
+ EXPECT_FALSE(normalizer.Normalize(kExactlyMaxPath, &result, &err));
+ EXPECT_TRUE(err.find("GetFullPathName") != string::npos);
+}
diff --git a/src/lexer.cc b/src/lexer.cc
index 3c6e70e..35ae97b 100644
--- a/src/lexer.cc
+++ b/src/lexer.cc
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.13.5 */
+/* Generated by re2c 0.16 */
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -126,305 +126,325 @@
unsigned char yych;
unsigned int yyaccept = 0;
static const unsigned char yybm[] = {
- 0, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 0, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 192, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 96, 96, 64,
- 96, 96, 96, 96, 96, 96, 96, 96,
- 96, 96, 64, 64, 64, 64, 64, 64,
- 64, 96, 96, 96, 96, 96, 96, 96,
- 96, 96, 96, 96, 96, 96, 96, 96,
- 96, 96, 96, 96, 96, 96, 96, 96,
- 96, 96, 96, 64, 64, 64, 64, 96,
- 64, 96, 96, 96, 96, 96, 96, 96,
- 96, 96, 96, 96, 96, 96, 96, 96,
- 96, 96, 96, 96, 96, 96, 96, 96,
- 96, 96, 96, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64,
+ 0, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 0, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 160, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 192, 192, 128,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 128, 128, 128, 128, 128, 128,
+ 128, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 128, 128, 128, 128, 192,
+ 128, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
};
-
yych = *p;
- if (yych <= 'Z') {
- if (yych <= '#') {
+ if (yybm[0+yych] & 32) {
+ goto yy9;
+ }
+ if (yych <= '^') {
+ if (yych <= ',') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy23;
- if (yych == '\n') goto yy7;
- goto yy25;
+ if (yych <= 0x00) goto yy2;
+ if (yych == '\n') goto yy6;
+ goto yy4;
} else {
- if (yych <= 0x1F) {
- if (yych <= '\r') goto yy6;
- goto yy25;
- } else {
- if (yych <= ' ') goto yy2;
- if (yych <= '"') goto yy25;
- goto yy4;
- }
+ if (yych <= '\r') goto yy8;
+ if (yych == '#') goto yy12;
+ goto yy4;
}
} else {
- if (yych <= '9') {
- if (yych <= ',') goto yy25;
- if (yych == '/') goto yy25;
- goto yy22;
+ if (yych <= ':') {
+ if (yych == '/') goto yy4;
+ if (yych <= '9') goto yy13;
+ goto yy16;
} else {
- if (yych <= '<') {
- if (yych <= ':') goto yy16;
- goto yy25;
+ if (yych <= '=') {
+ if (yych <= '<') goto yy4;
+ goto yy18;
} else {
- if (yych <= '=') goto yy14;
- if (yych <= '@') goto yy25;
- goto yy22;
+ if (yych <= '@') goto yy4;
+ if (yych <= 'Z') goto yy13;
+ goto yy4;
}
}
}
} else {
if (yych <= 'i') {
- if (yych <= 'a') {
- if (yych == '_') goto yy22;
- if (yych <= '`') goto yy25;
- goto yy22;
+ if (yych <= 'b') {
+ if (yych == '`') goto yy4;
+ if (yych <= 'a') goto yy13;
+ goto yy20;
} else {
- if (yych <= 'c') {
- if (yych <= 'b') goto yy9;
- goto yy22;
- } else {
- if (yych <= 'd') goto yy13;
- if (yych <= 'h') goto yy22;
- goto yy20;
- }
+ if (yych == 'd') goto yy21;
+ if (yych <= 'h') goto yy13;
+ goto yy22;
}
} else {
if (yych <= 'r') {
- if (yych == 'p') goto yy11;
- if (yych <= 'q') goto yy22;
- goto yy12;
+ if (yych == 'p') goto yy23;
+ if (yych <= 'q') goto yy13;
+ goto yy24;
} else {
if (yych <= 'z') {
- if (yych <= 's') goto yy21;
- goto yy22;
+ if (yych <= 's') goto yy25;
+ goto yy13;
} else {
- if (yych == '|') goto yy18;
- goto yy25;
+ if (yych == '|') goto yy26;
+ goto yy4;
}
}
}
}
yy2:
- yyaccept = 0;
- yych = *(q = ++p);
- goto yy73;
-yy3:
- { token = INDENT; break; }
+ ++p;
+ { token = TEOF; break; }
yy4:
- yyaccept = 1;
- yych = *(q = ++p);
- if (yych >= 0x01) goto yy68;
+ ++p;
yy5:
{ token = ERROR; break; }
yy6:
- yych = *++p;
- if (yych == '\n') goto yy65;
- goto yy5;
-yy7:
++p;
-yy8:
{ token = NEWLINE; break; }
+yy8:
+ yych = *++p;
+ if (yych == '\n') goto yy28;
+ goto yy5;
yy9:
- ++p;
- if ((yych = *p) == 'u') goto yy60;
- goto yy27;
-yy10:
- { token = IDENT; break; }
+ yyaccept = 0;
+ q = ++p;
+ yych = *p;
+ if (yybm[0+yych] & 32) {
+ goto yy9;
+ }
+ if (yych <= '\f') {
+ if (yych == '\n') goto yy6;
+ } else {
+ if (yych <= '\r') goto yy30;
+ if (yych == '#') goto yy32;
+ }
yy11:
- yych = *++p;
- if (yych == 'o') goto yy56;
- goto yy27;
+ { token = INDENT; break; }
yy12:
- yych = *++p;
- if (yych == 'u') goto yy52;
- goto yy27;
+ yyaccept = 1;
+ yych = *(q = ++p);
+ if (yych <= 0x00) goto yy5;
+ goto yy33;
yy13:
- yych = *++p;
- if (yych == 'e') goto yy45;
- goto yy27;
-yy14:
++p;
- { token = EQUALS; break; }
+ yych = *p;
+yy14:
+ if (yybm[0+yych] & 64) {
+ goto yy13;
+ }
+ { token = IDENT; break; }
yy16:
++p;
{ token = COLON; break; }
yy18:
++p;
- if ((yych = *p) == '|') goto yy43;
- { token = PIPE; break; }
+ { token = EQUALS; break; }
yy20:
yych = *++p;
- if (yych == 'n') goto yy36;
- goto yy27;
+ if (yych == 'u') goto yy36;
+ goto yy14;
yy21:
yych = *++p;
- if (yych == 'u') goto yy28;
- goto yy27;
+ if (yych == 'e') goto yy37;
+ goto yy14;
yy22:
yych = *++p;
- goto yy27;
+ if (yych == 'n') goto yy38;
+ goto yy14;
yy23:
- ++p;
- { token = TEOF; break; }
+ yych = *++p;
+ if (yych == 'o') goto yy39;
+ goto yy14;
+yy24:
+ yych = *++p;
+ if (yych == 'u') goto yy40;
+ goto yy14;
yy25:
yych = *++p;
- goto yy5;
+ if (yych == 'u') goto yy41;
+ goto yy14;
yy26:
++p;
- yych = *p;
-yy27:
- if (yybm[0+yych] & 32) {
- goto yy26;
- }
- goto yy10;
+ if ((yych = *p) == '|') goto yy42;
+ { token = PIPE; break; }
yy28:
- yych = *++p;
- if (yych != 'b') goto yy27;
- yych = *++p;
- if (yych != 'n') goto yy27;
- yych = *++p;
- if (yych != 'i') goto yy27;
- yych = *++p;
- if (yych != 'n') goto yy27;
- yych = *++p;
- if (yych != 'j') goto yy27;
- yych = *++p;
- if (yych != 'a') goto yy27;
- ++p;
- if (yybm[0+(yych = *p)] & 32) {
- goto yy26;
- }
- { token = SUBNINJA; break; }
-yy36:
- yych = *++p;
- if (yych != 'c') goto yy27;
- yych = *++p;
- if (yych != 'l') goto yy27;
- yych = *++p;
- if (yych != 'u') goto yy27;
- yych = *++p;
- if (yych != 'd') goto yy27;
- yych = *++p;
- if (yych != 'e') goto yy27;
- ++p;
- if (yybm[0+(yych = *p)] & 32) {
- goto yy26;
- }
- { token = INCLUDE; break; }
-yy43:
- ++p;
- { token = PIPE2; break; }
-yy45:
- yych = *++p;
- if (yych != 'f') goto yy27;
- yych = *++p;
- if (yych != 'a') goto yy27;
- yych = *++p;
- if (yych != 'u') goto yy27;
- yych = *++p;
- if (yych != 'l') goto yy27;
- yych = *++p;
- if (yych != 't') goto yy27;
- ++p;
- if (yybm[0+(yych = *p)] & 32) {
- goto yy26;
- }
- { token = DEFAULT; break; }
-yy52:
- yych = *++p;
- if (yych != 'l') goto yy27;
- yych = *++p;
- if (yych != 'e') goto yy27;
- ++p;
- if (yybm[0+(yych = *p)] & 32) {
- goto yy26;
- }
- { token = RULE; break; }
-yy56:
- yych = *++p;
- if (yych != 'o') goto yy27;
- yych = *++p;
- if (yych != 'l') goto yy27;
- ++p;
- if (yybm[0+(yych = *p)] & 32) {
- goto yy26;
- }
- { token = POOL; break; }
-yy60:
- yych = *++p;
- if (yych != 'i') goto yy27;
- yych = *++p;
- if (yych != 'l') goto yy27;
- yych = *++p;
- if (yych != 'd') goto yy27;
- ++p;
- if (yybm[0+(yych = *p)] & 32) {
- goto yy26;
- }
- { token = BUILD; break; }
-yy65:
++p;
{ token = NEWLINE; break; }
-yy67:
- ++p;
- yych = *p;
-yy68:
- if (yybm[0+yych] & 64) {
- goto yy67;
- }
- if (yych >= 0x01) goto yy70;
-yy69:
+yy30:
+ yych = *++p;
+ if (yych == '\n') goto yy28;
+yy31:
p = q;
- if (yyaccept <= 0) {
- goto yy3;
+ if (yyaccept == 0) {
+ goto yy11;
} else {
goto yy5;
}
-yy70:
+yy32:
+ ++p;
+ yych = *p;
+yy33:
+ if (yybm[0+yych] & 128) {
+ goto yy32;
+ }
+ if (yych <= 0x00) goto yy31;
++p;
{ continue; }
-yy72:
- yyaccept = 0;
- q = ++p;
- yych = *p;
-yy73:
- if (yybm[0+yych] & 128) {
- goto yy72;
- }
- if (yych <= '\f') {
- if (yych != '\n') goto yy3;
- } else {
- if (yych <= '\r') goto yy75;
- if (yych == '#') goto yy67;
- goto yy3;
- }
+yy36:
yych = *++p;
- goto yy8;
-yy75:
+ if (yych == 'i') goto yy44;
+ goto yy14;
+yy37:
+ yych = *++p;
+ if (yych == 'f') goto yy45;
+ goto yy14;
+yy38:
+ yych = *++p;
+ if (yych == 'c') goto yy46;
+ goto yy14;
+yy39:
+ yych = *++p;
+ if (yych == 'o') goto yy47;
+ goto yy14;
+yy40:
+ yych = *++p;
+ if (yych == 'l') goto yy48;
+ goto yy14;
+yy41:
+ yych = *++p;
+ if (yych == 'b') goto yy49;
+ goto yy14;
+yy42:
++p;
- if ((yych = *p) == '\n') goto yy65;
- goto yy69;
+ { token = PIPE2; break; }
+yy44:
+ yych = *++p;
+ if (yych == 'l') goto yy50;
+ goto yy14;
+yy45:
+ yych = *++p;
+ if (yych == 'a') goto yy51;
+ goto yy14;
+yy46:
+ yych = *++p;
+ if (yych == 'l') goto yy52;
+ goto yy14;
+yy47:
+ yych = *++p;
+ if (yych == 'l') goto yy53;
+ goto yy14;
+yy48:
+ yych = *++p;
+ if (yych == 'e') goto yy55;
+ goto yy14;
+yy49:
+ yych = *++p;
+ if (yych == 'n') goto yy57;
+ goto yy14;
+yy50:
+ yych = *++p;
+ if (yych == 'd') goto yy58;
+ goto yy14;
+yy51:
+ yych = *++p;
+ if (yych == 'u') goto yy60;
+ goto yy14;
+yy52:
+ yych = *++p;
+ if (yych == 'u') goto yy61;
+ goto yy14;
+yy53:
+ ++p;
+ if (yybm[0+(yych = *p)] & 64) {
+ goto yy13;
+ }
+ { token = POOL; break; }
+yy55:
+ ++p;
+ if (yybm[0+(yych = *p)] & 64) {
+ goto yy13;
+ }
+ { token = RULE; break; }
+yy57:
+ yych = *++p;
+ if (yych == 'i') goto yy62;
+ goto yy14;
+yy58:
+ ++p;
+ if (yybm[0+(yych = *p)] & 64) {
+ goto yy13;
+ }
+ { token = BUILD; break; }
+yy60:
+ yych = *++p;
+ if (yych == 'l') goto yy63;
+ goto yy14;
+yy61:
+ yych = *++p;
+ if (yych == 'd') goto yy64;
+ goto yy14;
+yy62:
+ yych = *++p;
+ if (yych == 'n') goto yy65;
+ goto yy14;
+yy63:
+ yych = *++p;
+ if (yych == 't') goto yy66;
+ goto yy14;
+yy64:
+ yych = *++p;
+ if (yych == 'e') goto yy68;
+ goto yy14;
+yy65:
+ yych = *++p;
+ if (yych == 'j') goto yy70;
+ goto yy14;
+yy66:
+ ++p;
+ if (yybm[0+(yych = *p)] & 64) {
+ goto yy13;
+ }
+ { token = DEFAULT; break; }
+yy68:
+ ++p;
+ if (yybm[0+(yych = *p)] & 64) {
+ goto yy13;
+ }
+ { token = INCLUDE; break; }
+yy70:
+ yych = *++p;
+ if (yych != 'a') goto yy14;
+ ++p;
+ if (yybm[0+(yych = *p)] & 64) {
+ goto yy13;
+ }
+ { token = SUBNINJA; break; }
}
}
@@ -487,49 +507,42 @@
0, 0, 0, 0, 0, 0, 0, 0,
};
yych = *p;
- if (yych <= ' ') {
- if (yych <= 0x00) goto yy82;
- if (yych <= 0x1F) goto yy84;
- } else {
- if (yych == '$') goto yy80;
- goto yy84;
- }
- ++p;
- yych = *p;
- goto yy92;
-yy79:
- { continue; }
-yy80:
- yych = *(q = ++p);
- if (yych == '\n') goto yy85;
- if (yych == '\r') goto yy87;
-yy81:
- { break; }
-yy82:
- ++p;
- { break; }
-yy84:
- yych = *++p;
- goto yy81;
-yy85:
- ++p;
- { continue; }
-yy87:
- yych = *++p;
- if (yych == '\n') goto yy89;
- p = q;
- goto yy81;
-yy89:
- ++p;
- { continue; }
-yy91:
- ++p;
- yych = *p;
-yy92:
if (yybm[0+yych] & 128) {
- goto yy91;
+ goto yy79;
}
- goto yy79;
+ if (yych <= 0x00) goto yy75;
+ if (yych == '$') goto yy82;
+ goto yy77;
+yy75:
+ ++p;
+ { break; }
+yy77:
+ ++p;
+yy78:
+ { break; }
+yy79:
+ ++p;
+ yych = *p;
+ if (yybm[0+yych] & 128) {
+ goto yy79;
+ }
+ { continue; }
+yy82:
+ yych = *(q = ++p);
+ if (yych == '\n') goto yy83;
+ if (yych == '\r') goto yy85;
+ goto yy78;
+yy83:
+ ++p;
+ { continue; }
+yy85:
+ yych = *++p;
+ if (yych == '\n') goto yy87;
+ p = q;
+ goto yy78;
+yy87:
+ ++p;
+ { continue; }
}
}
@@ -578,45 +591,24 @@
0, 0, 0, 0, 0, 0, 0, 0,
};
yych = *p;
- if (yych <= '@') {
- if (yych <= '.') {
- if (yych <= ',') goto yy97;
- } else {
- if (yych <= '/') goto yy97;
- if (yych >= ':') goto yy97;
- }
- } else {
- if (yych <= '_') {
- if (yych <= 'Z') goto yy95;
- if (yych <= '^') goto yy97;
- } else {
- if (yych <= '`') goto yy97;
- if (yych >= '{') goto yy97;
- }
+ if (yybm[0+yych] & 128) {
+ goto yy93;
}
-yy95:
- ++p;
- yych = *p;
- goto yy100;
-yy96:
- {
- out->assign(start, p - start);
- break;
- }
-yy97:
++p;
{
last_token_ = start;
return false;
}
-yy99:
+yy93:
++p;
yych = *p;
-yy100:
if (yybm[0+yych] & 128) {
- goto yy99;
+ goto yy93;
}
- goto yy96;
+ {
+ out->assign(start, p - start);
+ break;
+ }
}
}
@@ -636,72 +628,69 @@
{
unsigned char yych;
static const unsigned char yybm[] = {
- 0, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 0, 128, 128, 0, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 16, 128, 128, 128, 0, 128, 128, 128,
- 128, 128, 128, 128, 128, 224, 160, 128,
- 224, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 0, 128, 128, 128, 128, 128,
- 128, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 128, 128, 128, 128, 224,
- 128, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 128, 0, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
+ 0, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 0, 16, 16, 0, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 32, 16, 16, 16, 0, 16, 16, 16,
+ 16, 16, 16, 16, 16, 208, 144, 16,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 0, 16, 16, 16, 16, 16,
+ 16, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 16, 16, 16, 16, 208,
+ 16, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 16, 0, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
};
yych = *p;
- if (yych <= ' ') {
- if (yych <= '\n') {
- if (yych <= 0x00) goto yy110;
- if (yych >= '\n') goto yy107;
- } else {
- if (yych == '\r') goto yy105;
- if (yych >= ' ') goto yy107;
- }
- } else {
- if (yych <= '9') {
- if (yych == '$') goto yy109;
- } else {
- if (yych <= ':') goto yy107;
- if (yych == '|') goto yy107;
- }
+ if (yybm[0+yych] & 16) {
+ goto yy100;
}
+ if (yych <= '\r') {
+ if (yych <= 0x00) goto yy98;
+ if (yych <= '\n') goto yy103;
+ goto yy105;
+ } else {
+ if (yych <= ' ') goto yy103;
+ if (yych <= '$') goto yy107;
+ goto yy103;
+ }
+yy98:
+ ++p;
+ {
+ last_token_ = start;
+ return Error("unexpected EOF", err);
+ }
+yy100:
++p;
yych = *p;
- goto yy140;
-yy104:
+ if (yybm[0+yych] & 16) {
+ goto yy100;
+ }
{
eval->AddText(StringPiece(start, p - start));
continue;
}
-yy105:
- ++p;
- if ((yych = *p) == '\n') goto yy137;
- {
- last_token_ = start;
- return Error(DescribeLastError(), err);
- }
-yy107:
+yy103:
++p;
{
if (path) {
@@ -714,152 +703,121 @@
continue;
}
}
-yy109:
+yy105:
+ ++p;
+ if ((yych = *p) == '\n') goto yy108;
+ {
+ last_token_ = start;
+ return Error(DescribeLastError(), err);
+ }
+yy107:
yych = *++p;
- if (yych <= '-') {
- if (yych <= 0x1F) {
- if (yych <= '\n') {
- if (yych <= '\t') goto yy112;
- goto yy124;
- } else {
- if (yych == '\r') goto yy114;
- goto yy112;
- }
+ if (yybm[0+yych] & 64) {
+ goto yy120;
+ }
+ if (yych <= ' ') {
+ if (yych <= '\f') {
+ if (yych == '\n') goto yy112;
+ goto yy110;
} else {
- if (yych <= '#') {
- if (yych <= ' ') goto yy115;
- goto yy112;
- } else {
- if (yych <= '$') goto yy117;
- if (yych <= ',') goto yy112;
- goto yy119;
- }
+ if (yych <= '\r') goto yy115;
+ if (yych <= 0x1F) goto yy110;
+ goto yy116;
}
} else {
- if (yych <= 'Z') {
- if (yych <= '9') {
- if (yych <= '/') goto yy112;
- goto yy119;
- } else {
- if (yych <= ':') goto yy121;
- if (yych <= '@') goto yy112;
- goto yy119;
- }
+ if (yych <= '/') {
+ if (yych == '$') goto yy118;
+ goto yy110;
} else {
- if (yych <= '`') {
- if (yych == '_') goto yy119;
- goto yy112;
- } else {
- if (yych <= 'z') goto yy119;
- if (yych <= '{') goto yy123;
- goto yy112;
- }
+ if (yych <= ':') goto yy123;
+ if (yych <= '`') goto yy110;
+ if (yych <= '{') goto yy125;
+ goto yy110;
}
}
-yy110:
- ++p;
- {
- last_token_ = start;
- return Error("unexpected EOF", err);
- }
-yy112:
- ++p;
-yy113:
- {
- last_token_ = start;
- return Error("bad $-escape (literal $ must be written as $$)", err);
- }
-yy114:
- yych = *++p;
- if (yych == '\n') goto yy134;
- goto yy113;
-yy115:
- ++p;
- {
- eval->AddText(StringPiece(" ", 1));
- continue;
- }
-yy117:
- ++p;
- {
- eval->AddText(StringPiece("$", 1));
- continue;
- }
-yy119:
- ++p;
- yych = *p;
- goto yy133;
-yy120:
- {
- eval->AddSpecial(StringPiece(start + 1, p - start - 1));
- continue;
- }
-yy121:
- ++p;
- {
- eval->AddText(StringPiece(":", 1));
- continue;
- }
-yy123:
- yych = *(q = ++p);
- if (yybm[0+yych] & 32) {
- goto yy127;
- }
- goto yy113;
-yy124:
- ++p;
- yych = *p;
- if (yybm[0+yych] & 16) {
- goto yy124;
- }
- {
- continue;
- }
-yy127:
- ++p;
- yych = *p;
- if (yybm[0+yych] & 32) {
- goto yy127;
- }
- if (yych == '}') goto yy130;
- p = q;
- goto yy113;
-yy130:
- ++p;
- {
- eval->AddSpecial(StringPiece(start + 2, p - start - 3));
- continue;
- }
-yy132:
- ++p;
- yych = *p;
-yy133:
- if (yybm[0+yych] & 64) {
- goto yy132;
- }
- goto yy120;
-yy134:
- ++p;
- yych = *p;
- if (yych == ' ') goto yy134;
- {
- continue;
- }
-yy137:
+yy108:
++p;
{
if (path)
p = start;
break;
}
-yy139:
+yy110:
+ ++p;
+yy111:
+ {
+ last_token_ = start;
+ return Error("bad $-escape (literal $ must be written as $$)", err);
+ }
+yy112:
++p;
yych = *p;
-yy140:
- if (yybm[0+yych] & 128) {
- goto yy139;
+ if (yybm[0+yych] & 32) {
+ goto yy112;
}
- goto yy104;
+ {
+ continue;
+ }
+yy115:
+ yych = *++p;
+ if (yych == '\n') goto yy126;
+ goto yy111;
+yy116:
+ ++p;
+ {
+ eval->AddText(StringPiece(" ", 1));
+ continue;
+ }
+yy118:
+ ++p;
+ {
+ eval->AddText(StringPiece("$", 1));
+ continue;
+ }
+yy120:
+ ++p;
+ yych = *p;
+ if (yybm[0+yych] & 64) {
+ goto yy120;
+ }
+ {
+ eval->AddSpecial(StringPiece(start + 1, p - start - 1));
+ continue;
+ }
+yy123:
+ ++p;
+ {
+ eval->AddText(StringPiece(":", 1));
+ continue;
+ }
+yy125:
+ yych = *(q = ++p);
+ if (yybm[0+yych] & 128) {
+ goto yy129;
+ }
+ goto yy111;
+yy126:
+ ++p;
+ yych = *p;
+ if (yych == ' ') goto yy126;
+ {
+ continue;
+ }
+yy129:
+ ++p;
+ yych = *p;
+ if (yybm[0+yych] & 128) {
+ goto yy129;
+ }
+ if (yych == '}') goto yy132;
+ p = q;
+ goto yy111;
+yy132:
+ ++p;
+ {
+ eval->AddSpecial(StringPiece(start + 2, p - start - 3));
+ continue;
+ }
}
}
diff --git a/src/line_printer.cc b/src/line_printer.cc
index 2cd3e17..953982a 100644
--- a/src/line_printer.cc
+++ b/src/line_printer.cc
@@ -18,6 +18,9 @@
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
+#endif
#else
#include <unistd.h>
#include <sys/ioctl.h>
@@ -41,6 +44,20 @@
CONSOLE_SCREEN_BUFFER_INFO csbi;
smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
#endif
+ supports_color_ = smart_terminal_;
+ if (!supports_color_) {
+ const char* clicolor_force = getenv("CLICOLOR_FORCE");
+ supports_color_ = clicolor_force && string(clicolor_force) != "0";
+ }
+#ifdef _WIN32
+ // Try enabling ANSI escape sequence support on Windows 10 terminals.
+ if (supports_color_) {
+ DWORD mode;
+ if (GetConsoleMode(console_, &mode)) {
+ SetConsoleMode(console_, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+ }
+ }
+#endif
}
void LinePrinter::Print(string to_print, LineType type) {
@@ -82,7 +99,7 @@
// Limit output to width of the terminal if provided so we don't cause
// line-wrapping.
winsize size;
- if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
+ if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0) && size.ws_col) {
to_print = ElideMiddle(to_print, size.ws_col);
}
printf("%s", to_print.c_str());
diff --git a/src/line_printer.h b/src/line_printer.h
index 55225e5..92d4dc4 100644
--- a/src/line_printer.h
+++ b/src/line_printer.h
@@ -27,6 +27,8 @@
bool is_smart_terminal() const { return smart_terminal_; }
void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+ bool supports_color() const { return supports_color_; }
+
enum LineType {
FULL,
ELIDE
@@ -46,6 +48,9 @@
/// Whether we can do fancy terminal control codes.
bool smart_terminal_;
+ /// Whether we can use ISO 6429 (ANSI) color sequences.
+ bool supports_color_;
+
/// Whether the caret is at the beginning of a blank line.
bool have_blank_line_;
diff --git a/src/ninja.cc b/src/ninja.cc
index f9efc77..b608426 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -73,6 +73,10 @@
/// Whether phony cycles should warn or print an error.
bool phony_cycle_should_err;
+
+ /// Whether a depfile with multiple targets on separate lines should
+ /// warn or print an error.
+ bool depfile_distinct_target_lines_should_err;
};
/// The Ninja main() loads up a series of data structures; various tools need
@@ -154,7 +158,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.)
@@ -201,16 +205,16 @@
"if targets are unspecified, builds the 'default' target (see manual).\n"
"\n"
"options:\n"
-" --version print ninja version (\"%s\")\n"
+" --version print ninja version (\"%s\")\n"
+" -v, --verbose show all command lines while building\n"
"\n"
" -C DIR change to DIR before doing anything else\n"
" -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"
+" -j N run N jobs in parallel (0 means infinity) [default=%d on this system]\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"
@@ -387,7 +391,12 @@
// If we get here, the browse failed.
return 1;
}
-#endif // _WIN32
+#else
+int NinjaMain::ToolBrowse(const Options*, int, char**) {
+ Fatal("browse tool not supported on this platform");
+ return 1;
+}
+#endif
#if defined(_MSC_VER)
int NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) {
@@ -803,10 +812,8 @@
/// Returns a Tool, or NULL if Ninja should exit.
const Tool* ChooseTool(const string& tool_name) {
static const Tool kTools[] = {
-#if defined(NINJA_HAVE_BROWSE)
{ "browse", "browse dependency graph in a web browser",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse },
-#endif
#if defined(_MSC_VER)
{ "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)",
Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC },
@@ -909,7 +916,9 @@
if (name == "list") {
printf("warning flags:\n"
" dupbuild={err,warn} multiple build lines for one target\n"
-" phonycycle={err,warn} phony build statement references itself\n");
+" phonycycle={err,warn} phony build statement references itself\n"
+" depfilemulti={err,warn} depfile has multiple output paths on separate lines\n"
+ );
return false;
} else if (name == "dupbuild=err") {
options->dupe_edges_should_err = true;
@@ -923,6 +932,12 @@
} else if (name == "phonycycle=warn") {
options->phony_cycle_should_err = false;
return true;
+ } else if (name == "depfilemulti=err") {
+ options->depfile_distinct_target_lines_should_err = true;
+ return true;
+ } else if (name == "depfilemulti=warn") {
+ options->depfile_distinct_target_lines_should_err = false;
+ return true;
} else {
const char* suggestion =
SpellcheckString(name.c_str(), "dupbuild=err", "dupbuild=warn",
@@ -1102,6 +1117,7 @@
const option kLongOptions[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, OPT_VERSION },
+ { "verbose", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
@@ -1120,9 +1136,12 @@
case 'j': {
char* end;
int value = strtol(optarg, &end, 10);
- if (*end != 0 || value <= 0)
+ if (*end != 0 || value < 0)
Fatal("invalid -j parameter");
- config->parallelism = value;
+
+ // We want to run N jobs in parallel. For N = 0, INT_MAX
+ // is close enough to infinite for most sane builds.
+ config->parallelism = value > 0 ? value : INT_MAX;
break;
}
case 'k': {
@@ -1193,6 +1212,11 @@
if (exit_code >= 0)
exit(exit_code);
+ if (options.depfile_distinct_target_lines_should_err) {
+ config.depfile_parser_options.depfile_distinct_target_lines_action_ =
+ kDepfileDistinctTargetLinesActionError;
+ }
+
if (options.working_dir) {
// The formatting of this string, complete with funny quotes, is
// so Emacs can properly identify that the cwd has changed for
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-posix.cc b/src/subprocess-posix.cc
index 1de22c3..fc5543e 100644
--- a/src/subprocess-posix.cc
+++ b/src/subprocess-posix.cc
@@ -14,6 +14,7 @@
#include "subprocess.h"
+#include <sys/select.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
@@ -54,21 +55,25 @@
SetCloseOnExec(fd_);
posix_spawn_file_actions_t action;
- if (posix_spawn_file_actions_init(&action) != 0)
- Fatal("posix_spawn_file_actions_init: %s", strerror(errno));
+ int err = posix_spawn_file_actions_init(&action);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_init: %s", strerror(err));
- if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0)
- Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno));
+ err = posix_spawn_file_actions_addclose(&action, output_pipe[0]);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_addclose: %s", strerror(err));
posix_spawnattr_t attr;
- if (posix_spawnattr_init(&attr) != 0)
- Fatal("posix_spawnattr_init: %s", strerror(errno));
+ err = posix_spawnattr_init(&attr);
+ if (err != 0)
+ Fatal("posix_spawnattr_init: %s", strerror(err));
short flags = 0;
flags |= POSIX_SPAWN_SETSIGMASK;
- if (posix_spawnattr_setsigmask(&attr, &set->old_mask_) != 0)
- Fatal("posix_spawnattr_setsigmask: %s", strerror(errno));
+ err = posix_spawnattr_setsigmask(&attr, &set->old_mask_);
+ if (err != 0)
+ Fatal("posix_spawnattr_setsigmask: %s", strerror(err));
// Signals which are set to be caught in the calling process image are set to
// default action in the new process image, so no explicit
// POSIX_SPAWN_SETSIGDEF parameter is needed.
@@ -79,17 +84,21 @@
// No need to posix_spawnattr_setpgroup(&attr, 0), it's the default.
// Open /dev/null over stdin.
- if (posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY,
- 0) != 0) {
- Fatal("posix_spawn_file_actions_addopen: %s", strerror(errno));
+ err = posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY,
+ 0);
+ if (err != 0) {
+ Fatal("posix_spawn_file_actions_addopen: %s", strerror(err));
}
- if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1) != 0)
- Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
- if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2) != 0)
- Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
- if (posix_spawn_file_actions_addclose(&action, output_pipe[1]) != 0)
- Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno));
+ err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err));
+ err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err));
+ err = posix_spawn_file_actions_addclose(&action, output_pipe[1]);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_addclose: %s", strerror(err));
// In the console case, output_pipe is still inherited by the child and
// closed when the subprocess finishes, which then notifies ninja.
}
@@ -97,18 +106,22 @@
flags |= POSIX_SPAWN_USEVFORK;
#endif
- if (posix_spawnattr_setflags(&attr, flags) != 0)
- Fatal("posix_spawnattr_setflags: %s", strerror(errno));
+ err = posix_spawnattr_setflags(&attr, flags);
+ if (err != 0)
+ Fatal("posix_spawnattr_setflags: %s", strerror(err));
const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL };
- if (posix_spawn(&pid_, "/bin/sh", &action, &attr,
- const_cast<char**>(spawned_args), environ) != 0)
- Fatal("posix_spawn: %s", strerror(errno));
+ err = posix_spawn(&pid_, "/bin/sh", &action, &attr,
+ const_cast<char**>(spawned_args), environ);
+ if (err != 0)
+ Fatal("posix_spawn: %s", strerror(err));
- if (posix_spawnattr_destroy(&attr) != 0)
- Fatal("posix_spawnattr_destroy: %s", strerror(errno));
- if (posix_spawn_file_actions_destroy(&action) != 0)
- Fatal("posix_spawn_file_actions_destroy: %s", strerror(errno));
+ err = posix_spawnattr_destroy(&attr);
+ if (err != 0)
+ Fatal("posix_spawnattr_destroy: %s", strerror(err));
+ err = posix_spawn_file_actions_destroy(&action);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_destroy: %s", strerror(err));
close(output_pipe[1]);
return true;
diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc
index 5982b06..a4a7669 100644
--- a/src/subprocess-win32.cc
+++ b/src/subprocess-win32.cc
@@ -124,6 +124,10 @@
buf_ = "CreateProcess failed: The system cannot find the file "
"specified.\n";
return true;
+ } else if (error == ERROR_INVALID_PARAMETER) {
+ // This generally means that the command line was too long. Give extra
+ // context for this case.
+ Win32Fatal("CreateProcess", "is the command line too long?");
} else {
Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal
}
diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
index 0a8c206..6e487db 100644
--- a/src/subprocess_test.cc
+++ b/src/subprocess_test.cc
@@ -182,7 +182,7 @@
"cmd /c echo hi",
"cmd /c time /t",
#else
- "whoami",
+ "id -u",
"pwd",
#endif
};
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 61a038b..47a5de2 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;
}
@@ -346,9 +346,19 @@
return -errno;
}
+ struct stat st;
+ if (fstat(fileno(f), &st) < 0) {
+ err->assign(strerror(errno));
+ fclose(f);
+ return -errno;
+ }
+
+ // +1 is for the resize in ManifestParser::Load
+ contents->reserve(st.st_size + 1);
+
char buf[64 << 10];
size_t len;
- while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
+ while (!feof(f) && (len = fread(buf, 1, sizeof(buf), f)) > 0) {
contents->append(buf, len);
}
if (ferror(f)) {
@@ -432,8 +442,12 @@
return msg;
}
-void Win32Fatal(const char* function) {
- Fatal("%s: %s", function, GetLastErrorString().c_str());
+void Win32Fatal(const char* function, const char* hint) {
+ if (hint) {
+ Fatal("%s: %s (%s)", function, GetLastErrorString().c_str(), hint);
+ } else {
+ Fatal("%s: %s", function, GetLastErrorString().c_str());
+ }
}
#endif
@@ -573,7 +587,7 @@
string ElideMiddle(const string& str, size_t width) {
const int kMargin = 3; // Space for "...".
string result = str;
- if (result.size() + kMargin > width) {
+ if (result.size() > width) {
size_t elide_size = (width - kMargin) / 2;
result = result.substr(0, elide_size)
+ "..."
diff --git a/src/util.h b/src/util.h
index 4ee41a5..6a4a7a9 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, ...);
@@ -105,7 +119,7 @@
string GetLastErrorString();
/// Calls Fatal() with a function name and GetLastErrorString.
-NORETURN void Win32Fatal(const char* function);
+NORETURN void Win32Fatal(const char* function, const char* hint = NULL);
#endif
#endif // NINJA_UTIL_H_
diff --git a/src/util_test.cc b/src/util_test.cc
index b4b7516..d97b48c 100644
--- a/src/util_test.cc
+++ b/src/util_test.cc
@@ -419,10 +419,12 @@
TEST(ElideMiddle, NothingToElide) {
string input = "Nothing to elide in this short string.";
EXPECT_EQ(input, ElideMiddle(input, 80));
+ EXPECT_EQ(input, ElideMiddle(input, 38));
}
TEST(ElideMiddle, ElideInTheMiddle) {
string input = "01234567890123456789";
string elided = ElideMiddle(input, 10);
EXPECT_EQ("012...789", elided);
+ EXPECT_EQ("01234567...23456789", ElideMiddle(input, 19));
}