diff -Nru ninja-build-1.8.2/appveyor.yml ninja-build-1.9.0+om/appveyor.yml --- ninja-build-1.8.2/appveyor.yml 1970-01-01 00:00:00.000000000 +0000 +++ ninja-build-1.9.0+om/appveyor.yml 2019-01-30 18:58:59.000000000 +0000 @@ -0,0 +1,40 @@ +version: 1.0.{build} +image: Visual Studio 2017 + +environment: + CLICOLOR_FORCE: 1 + CHERE_INVOKING: 1 # Tell Bash to inherit the current working directory + matrix: + - MSYSTEM: MINGW64 + - MSYSTEM: MSVC + +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" + + python configure.py --bootstrap + + ninja.bootstrap.exe all + + ninja_test + + python misc/ninja_syntax_test.py + +test: off diff -Nru ninja-build-1.8.2/configure.py ninja-build-1.9.0+om/configure.py --- ninja-build-1.8.2/configure.py 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/configure.py 2019-01-30 18:58:59.000000000 +0000 @@ -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', @@ -256,7 +256,7 @@ if '--bootstrap' in configure_args: configure_args.remove('--bootstrap') n.variable('configure_args', ' '.join(configure_args)) -env_keys = set(['CXX', 'AR', 'CFLAGS', 'LDFLAGS']) +env_keys = set(['CXX', 'AR', 'CFLAGS', 'CXXFLAGS', 'LDFLAGS']) configure_env = dict((k, os.environ[k]) for k in os.environ if k in env_keys) if configure_env: config_str = ' '.join([k + '=' + pipes.quote(configure_env[k]) @@ -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 = [] @@ -397,6 +402,10 @@ if 'CFLAGS' in configure_env: cflags.append(configure_env['CFLAGS']) + ldflags.append(configure_env['CFLAGS']) +if 'CXXFLAGS' in configure_env: + cflags.append(configure_env['CXXFLAGS']) + ldflags.append(configure_env['CXXFLAGS']) n.variable('cflags', ' '.join(shell_escape(flag) for flag in cflags)) if 'LDFLAGS' in configure_env: ldflags.append(configure_env['LDFLAGS']) @@ -405,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. ) @@ -476,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', @@ -496,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') @@ -527,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() @@ -542,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', @@ -560,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)]) @@ -579,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 -Nru ninja-build-1.8.2/debian/changelog ninja-build-1.9.0+om/debian/changelog --- ninja-build-1.8.2/debian/changelog 2017-09-24 09:06:26.000000000 +0000 +++ ninja-build-1.9.0+om/debian/changelog 2019-10-28 22:55:01.000000000 +0000 @@ -1,3 +1,40 @@ +ninja-build (1.9.0+om-0ubu18.04.1~ppa) bionic; urgency=high + + * Switch build to python3. (Closes: #937142) + * Add python3 to Suggests as it's used for 'ninja -t browse'. + + -- Thorsten Stettin Mon, 28 Oct 2019 23:55:53 +0100 + +ninja-build (1.9.0-3) unstable; urgency=medium + + * Switch build to python3. (Closes: #937142) + * Add python3 to Suggests as it's used for 'ninja -t browse'. + + -- Felix Geyer Fri, 30 Aug 2019 19:28:53 +0200 + +ninja-build (1.9.0-2) unstable; urgency=medium + + * Make build reproducible again. (Closes: #932117) + - Fix man page sed pattern. + * Switch to debhelper compat level 12. + * Add a nodoc build profile. + + -- Felix Geyer Sat, 27 Jul 2019 10:04:12 +0200 + +ninja-build (1.9.0-1) unstable; urgency=medium + + * New upstream release. (Closes: #923545) + + [ Ondřej Nový ] + * d/copyright: Use https protocol in Format field + * d/control: Set Vcs-* to salsa.debian.org + + [ Felix Geyer ] + * Drop Conflicts: ninja as that package has been removed. + * Drop obsolete /etc/bash-completion.d/ninja removal maintscript. + + -- Felix Geyer Sat, 13 Jul 2019 21:25:34 +0200 + ninja-build (1.8.2-1) unstable; urgency=medium * New upstream release. diff -Nru ninja-build-1.8.2/debian/compat ninja-build-1.9.0+om/debian/compat --- ninja-build-1.8.2/debian/compat 2016-02-04 21:19:44.000000000 +0000 +++ ninja-build-1.9.0+om/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -9 diff -Nru ninja-build-1.8.2/debian/control ninja-build-1.9.0+om/debian/control --- ninja-build-1.8.2/debian/control 2017-06-18 07:43:24.000000000 +0000 +++ ninja-build-1.9.0+om/debian/control 2019-10-28 22:48:19.000000000 +0000 @@ -1,25 +1,26 @@ Source: ninja-build Section: devel Priority: optional -Maintainer: Felix Geyer -Build-Depends: asciidoc, - debhelper (>= 9), - docbook-xml, - docbook-xsl, - help2man, - python, +Maintainer: Thorsten Stettin +XSBC-Original-Maintainer: Felix Geyer +Build-Depends: asciidoc , + debhelper-compat (= 12), + docbook-xml , + docbook-xsl , + help2man , + python3, re2c, - xsltproc + xsltproc , Homepage: https://ninja-build.org/ -Vcs-Git: https://anonscm.debian.org/git/collab-maint/ninja-build.git -Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/ninja-build.git +Vcs-Git: https://salsa.debian.org/debian/ninja-build.git +Vcs-Browser: https://salsa.debian.org/debian/ninja-build Standards-Version: 3.9.6 Package: ninja-build Architecture: any Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} -Conflicts: ninja +Suggests: python3 Description: small build system closest in spirit to Make Ninja is yet another build system. It takes as input the interdependencies of files (typically source code and output executables) and orchestrates diff -Nru ninja-build-1.8.2/debian/copyright ninja-build-1.9.0+om/debian/copyright --- ninja-build-1.8.2/debian/copyright 2016-02-07 10:41:50.000000000 +0000 +++ ninja-build-1.9.0+om/debian/copyright 2018-11-30 20:11:18.000000000 +0000 @@ -1,4 +1,4 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: ninja Upstream-Contact: martine@danga.com Source: https://ninja-build.org/ diff -Nru ninja-build-1.8.2/debian/ninja-build.doc-base ninja-build-1.9.0+om/debian/ninja-build.doc-base --- ninja-build-1.8.2/debian/ninja-build.doc-base 2016-02-04 21:19:44.000000000 +0000 +++ ninja-build-1.9.0+om/debian/ninja-build.doc-base 2019-07-20 10:22:14.000000000 +0000 @@ -7,6 +7,6 @@ Section: Programming Format: html -Index: /usr/share/doc/ninja-build/html/manual.html -Files: /usr/share/doc/ninja-build/html/* +Index: /usr/share/doc/ninja-build/manual.html +Files: /usr/share/doc/ninja-build/manual.html diff -Nru ninja-build-1.8.2/debian/ninja-build.docs ninja-build-1.9.0+om/debian/ninja-build.docs --- ninja-build-1.8.2/debian/ninja-build.docs 1970-01-01 00:00:00.000000000 +0000 +++ ninja-build-1.9.0+om/debian/ninja-build.docs 2019-07-20 10:05:43.000000000 +0000 @@ -0,0 +1 @@ +doc/manual.html diff -Nru ninja-build-1.8.2/debian/ninja-build.install ninja-build-1.9.0+om/debian/ninja-build.install --- ninja-build-1.8.2/debian/ninja-build.install 2016-11-11 15:29:15.000000000 +0000 +++ ninja-build-1.9.0+om/debian/ninja-build.install 2019-07-20 10:05:40.000000000 +0000 @@ -1,4 +1,3 @@ -doc/manual.html usr/share/doc/ninja-build/html misc/bash-completion usr/share/bash-completion/completions/ misc/ninja-mode.el usr/share/emacs/site-lisp misc/ninja.vim usr/share/vim/addons/syntax diff -Nru ninja-build-1.8.2/debian/ninja-build.maintscript ninja-build-1.9.0+om/debian/ninja-build.maintscript --- ninja-build-1.8.2/debian/ninja-build.maintscript 2016-02-07 10:41:50.000000000 +0000 +++ ninja-build-1.9.0+om/debian/ninja-build.maintscript 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -rm_conffile /etc/bash_completion.d/ninja 1.6.0-1~ diff -Nru ninja-build-1.8.2/debian/rules ninja-build-1.9.0+om/debian/rules --- ninja-build-1.8.2/debian/rules 2017-09-24 08:58:55.000000000 +0000 +++ ninja-build-1.9.0+om/debian/rules 2019-08-29 17:20:54.000000000 +0000 @@ -8,13 +8,16 @@ dh $@ override_dh_auto_configure: - ./configure.py --bootstrap --verbose + python3 ./configure.py --bootstrap --verbose override_dh_auto_build: ./ninja -v all +ifeq (,$(filter nodoc,$(DEB_BUILD_PROFILES))) ./ninja -v manual - help2man --include=debian/ninja.1.in --output=build/ninja.1 --no-info --no-discard-stderr ./ninja - sed -i 's/\(default=\)[0-9]\+, \(derived from CPUs available\)/\1\2/g' build/ninja.1 + help2man --include=debian/ninja.1.in --output=build/ninja.1.org --no-info --no-discard-stderr ./ninja + sed 's/\(default=\)[0-9]\+ on this system/\1\#CPUs/' build/ninja.1.org > build/ninja.1 + if cmp --silent build/ninja.1.org build/ninja.1; then echo "sed pattern not found"; exit 1; fi +endif override_dh_auto_test: ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) @@ -43,4 +46,3 @@ debian/ninja-build/usr/share/bash-completion/completions/ninja mv debian/ninja-build/usr/share/zsh/vendor-completions/zsh-completion \ debian/ninja-build/usr/share/zsh/vendor-completions/_ninja - diff -Nru ninja-build-1.8.2/doc/manual.asciidoc ninja-build-1.9.0+om/doc/manual.asciidoc --- ninja-build-1.8.2/doc/manual.asciidoc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/doc/manual.asciidoc 2019-01-30 18:58:59.000000000 +0000 @@ -1,6 +1,6 @@ The Ninja build system ====================== -v1.8.2, Sep 2017 +v1.9.0, Jan 2019 Introduction @@ -155,11 +155,10 @@ Ninja's benefit comes from using it in conjunction with a smarter meta-build system. -http://code.google.com/p/gyp/[gyp]:: The meta-build system used to +https://gn.googlesource.com/gn/[gn]:: The meta-build system used to generate build files for Google Chrome and related projects (v8, -node.js). gyp can generate Ninja files for all platforms supported by -Chrome. See the -https://chromium.googlesource.com/chromium/src/+/master/docs/ninja_build.md[Chromium Ninja documentation for more details]. +node.js), as well as Google Fuschia. gn can generate Ninja files for +all platforms supported by Chrome. https://cmake.org/[CMake]:: A widely used meta-build system that can generate Ninja files on Linux as of CMake version 2.8.8. Newer versions @@ -594,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. + ---- @@ -881,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 -Nru ninja-build-1.8.2/.gitignore ninja-build-1.9.0+om/.gitignore --- ninja-build-1.8.2/.gitignore 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/.gitignore 2019-01-30 18:58:59.000000000 +0000 @@ -32,3 +32,6 @@ # Ninja output .ninja_deps .ninja_log + +# Visual Studio Code project files +/.vscode/ diff -Nru ninja-build-1.8.2/HACKING.md ninja-build-1.9.0+om/HACKING.md --- ninja-build-1.8.2/HACKING.md 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/HACKING.md 2019-01-30 18:58:59.000000000 +0000 @@ -13,14 +13,50 @@ Ninja is built using itself. To bootstrap the first binary, run the configure script as `./configure.py --bootstrap`. This first compiles all non-test source files together, then re-builds Ninja using itself. -You should end up with a `ninja` binary (or `ninja.exe`) in the source root. +You should end up with a `ninja` binary (or `ninja.exe`) in the project root. #### Windows 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 @@ -73,17 +109,9 @@ ## Testing performance impact of changes -If you have a Chrome build handy, it's a good test case. Otherwise, -[the github downoads page](https://github.com/ninja-build/ninja/releases) -has a copy of the Chrome build files (and depfiles). You can untar -that, then run - - path/to/my/ninja chrome - -and compare that against a baseline Ninja. - -There's a script at `misc/measure.py` that repeatedly runs a command like -the above (to address variance) and summarizes its runtime. E.g. +If you have a Chrome build handy, it's a good test case. There's a +script at `misc/measure.py` that repeatedly runs a command (to address +variance) and summarizes its runtime. E.g. path/to/misc/measure.py path/to/my/ninja chrome @@ -95,7 +123,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 -Nru ninja-build-1.8.2/misc/ninja-mode.el ninja-build-1.9.0+om/misc/ninja-mode.el --- ninja-build-1.8.2/misc/ninja-mode.el 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/misc/ninja-mode.el 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/misc/ninja_syntax.py ninja-build-1.9.0+om/misc/ninja_syntax.py --- ninja-build-1.8.2/misc/ninja_syntax.py 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/misc/ninja_syntax.py 2019-01-30 18:58:59.000000000 +0000 @@ -21,7 +21,7 @@ def newline(self): self.output.write('\n') - def comment(self, text, has_path=False): + def comment(self, text): for line in textwrap.wrap(text, self.width - 2, break_long_words=False, break_on_hyphens=False): self.output.write('# ' + line + '\n') @@ -60,7 +60,7 @@ self.variable('deps', deps, indent=1) def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, - variables=None, implicit_outputs=None): + variables=None, implicit_outputs=None, pool=None): outputs = as_list(outputs) out_outputs = [escape_path(x) for x in outputs] all_inputs = [escape_path(x) for x in as_list(inputs)] @@ -81,6 +81,8 @@ self._line('build %s: %s' % (' '.join(out_outputs), ' '.join([rule] + all_inputs))) + if pool is not None: + self._line(' pool = %s' % pool) if variables: if isinstance(variables, dict): diff -Nru ninja-build-1.8.2/misc/ninja_syntax_test.py ninja-build-1.9.0+om/misc/ninja_syntax_test.py --- ninja-build-1.8.2/misc/ninja_syntax_test.py 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/misc/ninja_syntax_test.py 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/misc/ninja.vim ninja-build-1.9.0+om/misc/ninja.vim --- ninja-build-1.8.2/misc/ninja.vim 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/misc/ninja.vim 2019-01-30 18:58:59.000000000 +0000 @@ -1,8 +1,8 @@ " ninja build file syntax. " Language: ninja build file as described at " http://ninja-build.org/manual.html -" Version: 1.4 -" Last Change: 2014/05/13 +" Version: 1.5 +" Last Change: 2018/04/05 " Maintainer: Nicolas Weber " Version 1.4 of this script is in the upstream vim repository and will be " included in the next vim release. If you change this, please send your change @@ -21,7 +21,10 @@ syn case match -syn match ninjaComment /#.*/ contains=@Spell +" Comments are only matched when the # is at the beginning of the line (with +" optional whitespace), as long as the prior line didn't end with a $ +" continuation. +syn match ninjaComment /\(\$\n\)\@ 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 -Nru ninja-build-1.8.2/misc/zsh-completion ninja-build-1.9.0+om/misc/zsh-completion --- ninja-build-1.8.2/misc/zsh-completion 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/misc/zsh-completion 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/RELEASING ninja-build-1.9.0+om/RELEASING --- ninja-build-1.8.2/RELEASING 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/RELEASING 2019-01-30 18:58:59.000000000 +0000 @@ -1,19 +1,20 @@ Notes to myself on all the steps to make for a Ninja release. Push new release branch: -1. Consider sending a heads-up to the ninja-build mailing list first -2. Make sure branches 'master' and 'release' are synced up locally -3. update src/version.cc with new version (with ".git"), then +1. Run afl-fuzz for a day or so (see HACKING.md) and run ninja_test +2. Consider sending a heads-up to the ninja-build mailing list first +3. Make sure branches 'master' and 'release' are synced up locally +4. Update src/version.cc with new version (with ".git"), then git commit -am 'mark this 1.5.0.git' -4. git checkout release; git merge master -5. fix version number in src/version.cc (it will likely conflict in the above) -6. fix version in doc/manual.asciidoc (exists only on release branch) -7. commit, tag, push (don't forget to push --tags) +5. git checkout release; git merge master +6. Fix version number in src/version.cc (it will likely conflict in the above) +7. Fix version in doc/manual.asciidoc (exists only on release branch) +8. commit, tag, push (don't forget to push --tags) git commit -am v1.5.0; git push origin release git tag v1.5.0; git push --tags # Push the 1.5.0.git change on master too: git checkout master; git push origin master -8. construct release notes from prior notes +9. Construct release notes from prior notes credits: git shortlog -s --no-merges REV.. Release on github: diff -Nru ninja-build-1.8.2/src/browse.cc ninja-build-1.9.0+om/src/browse.cc --- ninja-build-1.8.2/src/browse.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/browse.cc 2019-01-30 18:58:59.000000000 +0000 @@ -14,6 +14,7 @@ #include "browse.h" +#include #include #include #include @@ -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 -Nru ninja-build-1.8.2/src/browse.py ninja-build-1.9.0+om/src/browse.py --- ninja-build-1.8.2/src/browse.py 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/browse.py 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/build.cc ninja-build-1.9.0+om/src/build.cc --- ninja-build-1.8.2/src/build.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/build.cc 2019-01-30 18:58:59.000000000 +0000 @@ -154,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; @@ -318,18 +317,18 @@ return false; // Don't need to do anything. // If an entry in want_ does not already exist for edge, create an entry which - // maps to false, indicating that we do not want to build this entry itself. - pair::iterator, bool> want_ins = - want_.insert(make_pair(edge, false)); - bool& want = want_ins.first->second; + // maps to kWantNothing, indicating that we do not want to build this entry itself. + pair::iterator, bool> want_ins = + want_.insert(make_pair(edge, kWantNothing)); + Want& want = want_ins.first->second; // If we do need to build edge and we haven't already marked it as wanted, // mark it now. - if (node->dirty() && !want) { - want = true; + if (node->dirty() && want == kWantNothing) { + want = kWantToStart; ++wanted_edges_; if (edge->AllInputsReady()) - ScheduleWork(edge); + ScheduleWork(want_ins.first); if (!edge->is_phony()) ++command_edges_; } @@ -355,30 +354,32 @@ return edge; } -void Plan::ScheduleWork(Edge* edge) { - set::iterator e = ready_.lower_bound(edge); - if (e != ready_.end() && !ready_.key_comp()(edge, *e)) { +void Plan::ScheduleWork(map::iterator want_e) { + if (want_e->second == kWantToFinish) { // This edge has already been scheduled. We can get here again if an edge // and one of its dependencies share an order-only input, or if a node // duplicates an out edge (see https://github.com/ninja-build/ninja/pull/519). // Avoid scheduling the work again. return; } + assert(want_e->second == kWantToStart); + want_e->second = kWantToFinish; + Edge* edge = want_e->first; Pool* pool = edge->pool(); if (pool->ShouldDelayEdge()) { pool->DelayEdge(edge); pool->RetrieveReadyEdges(&ready_); } else { pool->EdgeScheduled(*edge); - ready_.insert(e, edge); + ready_.insert(edge); } } void Plan::EdgeFinished(Edge* edge, EdgeResult result) { - map::iterator e = want_.find(edge); + map::iterator e = want_.find(edge); assert(e != want_.end()); - bool directly_wanted = e->second; + bool directly_wanted = e->second != kWantNothing; // See if this job frees up any delayed jobs. if (directly_wanted) @@ -405,14 +406,14 @@ // See if we we want any edges from this node. for (vector::const_iterator oe = node->out_edges().begin(); oe != node->out_edges().end(); ++oe) { - map::iterator want_e = want_.find(*oe); + map::iterator want_e = want_.find(*oe); if (want_e == want_.end()) continue; // See if the edge is now ready. if ((*oe)->AllInputsReady()) { - if (want_e->second) { - ScheduleWork(*oe); + if (want_e->second != kWantNothing) { + ScheduleWork(want_e); } else { // We do not need to build this edge, but we might need to build one of // its dependents. @@ -428,8 +429,8 @@ for (vector::const_iterator oe = node->out_edges().begin(); oe != node->out_edges().end(); ++oe) { // Don't process edges that we don't actually want. - map::iterator want_e = want_.find(*oe); - if (want_e == want_.end() || !want_e->second) + map::iterator want_e = want_.find(*oe); + if (want_e == want_.end() || want_e->second == kWantNothing) continue; // Don't attempt to clean an edge if it failed to load deps. @@ -441,7 +442,12 @@ vector::iterator begin = (*oe)->inputs_.begin(), end = (*oe)->inputs_.end() - (*oe)->order_only_deps_; - if (find_if(begin, end, mem_fun(&Node::dirty)) == end) { +#if __cplusplus < 201703L +#define MEM_FN mem_fun +#else +#define MEM_FN mem_fn // mem_fun was removed in C++17. +#endif + if (find_if(begin, end, MEM_FN(&Node::dirty)) == end) { // Recompute most_recent_input. Node* most_recent_input = NULL; for (vector::iterator i = begin; i != end; ++i) { @@ -464,7 +470,7 @@ return false; } - want_e->second = false; + want_e->second = kWantNothing; --wanted_edges_; if (!(*oe)->is_phony()) --command_edges_; @@ -476,8 +482,8 @@ void Plan::Dump() { printf("pending: %d\n", (int)want_.size()); - for (map::iterator e = want_.begin(); e != want_.end(); ++e) { - if (e->second) + for (map::iterator e = want_.begin(); e != want_.end(); ++e) { + if (e->second != kWantNothing) printf("want "); e->first->Dump(); } @@ -551,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); } @@ -900,7 +907,7 @@ if (content.empty()) return true; - DepfileParser deps; + DepfileParser deps(config_.depfile_parser_options); if (!deps.Parse(&content, err)) return false; diff -Nru ninja-build-1.8.2/src/build.h ninja-build-1.9.0+om/src/build.h --- ninja-build-1.8.2/src/build.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/build.h 2019-01-30 18:58:59.000000000 +0000 @@ -23,6 +23,7 @@ #include #include +#include "depfile_parser.h" #include "graph.h" // XXX needed for DependencyScan; should rearrange. #include "exit_status.h" #include "line_printer.h" @@ -78,17 +79,29 @@ bool AddSubTarget(Node* node, Node* dependent, string* err); void NodeFinished(Node* node); + /// Enumerate possible steps we want for an edge. + enum Want + { + /// We do not want to build the edge, but we might want to build one of + /// its dependents. + kWantNothing, + /// We want to build the edge, but have not yet scheduled it. + kWantToStart, + /// We want to build the edge, have scheduled it, and are waiting + /// for it to complete. + kWantToFinish + }; + /// Submits a ready edge as a candidate for execution. /// The edge may be delayed from running, for example if it's a member of a /// currently-full pool. - void ScheduleWork(Edge* edge); + void ScheduleWork(map::iterator want_e); /// Keep track of which edges we want to build in this plan. If this map does /// not contain an entry for an edge, we do not want to build the entry or its - /// dependents. If an entry maps to false, we do not want to build it, but we - /// might want to build one of its dependents. If the entry maps to true, we - /// want to build it. - map want_; + /// dependents. If it does contain an entry, the enumeration indicates what + /// we want for the edge. + map want_; set ready_; @@ -139,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. @@ -178,7 +192,11 @@ State* state_; const BuildConfig& config_; Plan plan_; +#if __cplusplus < 201703L auto_ptr command_runner_; +#else + unique_ptr command_runner_; // auto_ptr was removed in C++17. +#endif BuildStatus* status_; private: diff -Nru ninja-build-1.8.2/src/build_log.cc ninja-build-1.9.0+om/src/build_log.cc --- ninja-build-1.8.2/src/build_log.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/build_log.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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; }; @@ -167,6 +176,9 @@ if (log_file_) { if (!WriteEntry(log_file_, *log_entry)) return false; + if (fflush(log_file_) != 0) { + return false; + } } } return true; @@ -290,7 +302,7 @@ if (!end) continue; *end = 0; - restat_mtime = atol(start); + restat_mtime = strtoll(start, NULL, 10); start = end + 1; end = (char*)memchr(start, kFieldSeparator, line_end - start); @@ -353,7 +365,7 @@ } bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) { - return fprintf(f, "%d\t%d\t%d\t%s\t%" PRIx64 "\n", + return fprintf(f, "%d\t%d\t%" PRId64 "\t%s\t%" PRIx64 "\n", entry.start_time, entry.end_time, entry.mtime, entry.output.c_str(), entry.command_hash) > 0; } diff -Nru ninja-build-1.8.2/src/clean.cc ninja-build-1.9.0+om/src/clean.cc --- ninja-build-1.8.2/src/clean.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/clean.cc 2019-01-30 18:58:59.000000000 +0000 @@ -101,6 +101,7 @@ printf("\n"); else printf(" "); + fflush(stdout); } void Cleaner::PrintFooter() { @@ -180,15 +181,22 @@ Reset(); PrintHeader(); for (int i = 0; i < target_count; ++i) { - const char* target_name = targets[i]; - Node* target = state_->LookupNode(target_name); - if (target) { - if (IsVerbose()) - printf("Target %s\n", target_name); - DoCleanTarget(target); - } else { - Error("unknown target '%s'", target_name); + string target_name = targets[i]; + uint64_t slash_bits; + string err; + if (!CanonicalizePath(&target_name, &slash_bits, &err)) { + Error("failed to canonicalize '%s': %s", target_name.c_str(), err.c_str()); status_ = 1; + } else { + Node* target = state_->LookupNode(target_name); + if (target) { + if (IsVerbose()) + printf("Target %s\n", target_name.c_str()); + DoCleanTarget(target); + } else { + Error("unknown target '%s'", target_name.c_str()); + status_ = 1; + } } } PrintFooter(); diff -Nru ninja-build-1.8.2/src/depfile_parser.cc ninja-build-1.9.0+om/src/depfile_parser.cc --- ninja-build-1.8.2/src/depfile_parser.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/depfile_parser.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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 (yych <= '$') { - if (yych <= ' ') { - if (yych <= 0x00) goto yy7; - goto yy9; - } else { - if (yych <= '!') goto yy5; - if (yych <= '#') goto yy9; - goto yy4; - } + if (yybm[0+yych] & 128) { + goto yy9; + } + if (yych <= '\r') { + if (yych <= '\t') { + if (yych >= 0x01) goto yy4; } else { - if (yych <= '*') { - if (yych <= '\'') goto yy9; - if (yych <= ')') goto yy5; - goto yy9; - } else { - if (yych <= ':') goto yy5; - if (yych <= '<') goto yy9; - goto yy5; - } + if (yych <= '\n') goto yy6; + if (yych <= '\f') goto yy4; + goto yy8; } } 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; - } + if (yych <= '$') { + if (yych <= '#') goto yy4; + goto yy12; } 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: +yy12: yych = *++in; - goto yy3; -yy10: - ++in; - yych = *in; -yy11: - if (yybm[0+yych] & 128) { - goto yy10; + 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; + } } - goto yy6; -yy12: +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 -Nru ninja-build-1.8.2/src/depfile_parser.h ninja-build-1.9.0+om/src/depfile_parser.h --- ninja-build-1.8.2/src/depfile_parser.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/depfile_parser.h 2019-01-30 18:58:59.000000000 +0000 @@ -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 ins_; + DepfileParserOptions options_; }; #endif // NINJA_DEPFILE_PARSER_H_ diff -Nru ninja-build-1.8.2/src/depfile_parser.in.cc ninja-build-1.9.0+om/src/depfile_parser.in.cc --- ninja-build-1.8.2/src/depfile_parser.in.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/depfile_parser.in.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/depfile_parser_test.cc ninja-build-1.9.0+om/src/depfile_parser_test.cc --- ninja-build-1.8.2/src/depfile_parser_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/depfile_parser_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/deps_log.cc ninja-build-1.9.0+om/src/deps_log.cc --- ninja-build-1.8.2/src/deps_log.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/deps_log.cc 2019-01-30 18:58:59.000000000 +0000 @@ -20,6 +20,9 @@ #include #ifndef _WIN32 #include +#elif defined(_MSC_VER) && (_MSC_VER < 1900) +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; #endif #include "graph.h" @@ -30,7 +33,7 @@ // The version is stored as 4 bytes after the signature and also serves as a // byte order mark. Signature and version combined are 16 bytes long. const char kFileSignature[] = "# ninjadeps\n"; -const int kCurrentVersion = 3; +const int kCurrentVersion = 4; // Record size is currently limited to less than the full 32 bit, due to // internal buffers having to have this size. @@ -124,7 +127,7 @@ return true; // Update on-disk representation. - unsigned size = 4 * (1 + 1 + node_count); + unsigned size = 4 * (1 + 2 + node_count); if (size > kMaxRecordSize) { errno = ERANGE; return false; @@ -135,8 +138,11 @@ int id = node->id(); if (fwrite(&id, 4, 1, file_) < 1) return false; - int timestamp = mtime; - if (fwrite(×tamp, 4, 1, file_) < 1) + uint32_t mtime_part = static_cast(mtime & 0xffffffff); + if (fwrite(&mtime_part, 4, 1, file_) < 1) + return false; + mtime_part = static_cast((mtime >> 32) & 0xffffffff); + if (fwrite(&mtime_part, 4, 1, file_) < 1) return false; for (int i = 0; i < node_count; ++i) { id = nodes[i]->id(); @@ -209,7 +215,7 @@ bool is_deps = (size >> 31) != 0; size = size & 0x7FFFFFFF; - if (fread(buf, size, 1, f) < 1 || size > kMaxRecordSize) { + if (size > kMaxRecordSize || fread(buf, size, 1, f) < 1) { read_failed = true; break; } @@ -218,9 +224,11 @@ assert(size % 4 == 0); int* deps_data = reinterpret_cast(buf); int out_id = deps_data[0]; - int mtime = deps_data[1]; - deps_data += 2; - int deps_count = (size / 4) - 2; + TimeStamp mtime; + mtime = (TimeStamp)(((uint64_t)(unsigned int)deps_data[2] << 32) | + (uint64_t)(unsigned int)deps_data[1]); + deps_data += 3; + int deps_count = (size / 4) - 3; Deps* deps = new Deps(mtime, deps_count); for (int i = 0; i < deps_count; ++i) { diff -Nru ninja-build-1.8.2/src/deps_log.h ninja-build-1.9.0+om/src/deps_log.h --- ninja-build-1.8.2/src/deps_log.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/deps_log.h 2019-01-30 18:58:59.000000000 +0000 @@ -57,7 +57,9 @@ /// one's complement of the expected index of the record (to detect /// concurrent writes of multiple ninja processes to the log). /// dependency records are an array of 4-byte integers -/// [output path id, output path mtime, input path id, input path id...] +/// [output path id, +/// output path mtime (lower 4 bytes), output path mtime (upper 4 bytes), +/// input path id, input path id...] /// (The mtime is compared against the on-disk output path mtime /// to verify the stored data is up-to-date.) /// If two records reference the same output the latter one in the file @@ -75,10 +77,10 @@ // Reading (startup-time) interface. struct Deps { - Deps(int mtime, int node_count) + Deps(int64_t mtime, int node_count) : mtime(mtime), node_count(node_count), nodes(new Node*[node_count]) {} ~Deps() { delete [] nodes; } - int mtime; + TimeStamp mtime; int node_count; Node** nodes; }; diff -Nru ninja-build-1.8.2/src/deps_log_test.cc ninja-build-1.9.0+om/src/deps_log_test.cc --- ninja-build-1.8.2/src/deps_log_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/deps_log_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/disk_interface.cc ninja-build-1.9.0+om/src/disk_interface.cc --- ninja-build-1.8.2/src/disk_interface.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/disk_interface.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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; @@ -61,17 +62,16 @@ TimeStamp TimeStampFromFileTime(const FILETIME& filetime) { // FILETIME is in 100-nanosecond increments since the Windows epoch. // We don't much care about epoch correctness but we do want the - // resulting value to fit in an integer. + // resulting value to fit in a 64-bit integer. uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | ((uint64_t)filetime.dwLowDateTime); - mtime /= 1000000000LL / 100; // 100ns -> s. - mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years). - return (TimeStamp)mtime; + // 1600 epoch -> 2000 epoch (subtract 400 years). + return (TimeStamp)mtime - 12622770400LL * (1000000000LL / 100); } TimeStamp StatSingleFile(const string& path, string* err) { WIN32_FILE_ATTRIBUTE_DATA attrs; - if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) { + if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) { DWORD win_err = GetLastError(); if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) return 0; @@ -113,6 +113,11 @@ } do { string lowername = ffd.cFileName; + if (lowername == "..") { + // Seems to just copy the timestamp for ".." from ".", which is wrong. + // This is the case at least on NTFS under Windows 7. + continue; + } transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower); stamps->insert(make_pair(lowername, TimeStampFromFileTime(ffd.ftLastWriteTime))); @@ -165,6 +170,11 @@ string dir = DirName(path); string base(path.substr(dir.size() ? dir.size() + 1 : 0)); + if (base == "..") { + // StatAllFilesInDir does not report any information for base = "..". + base = "."; + dir = path; + } transform(dir.begin(), dir.end(), dir.begin(), ::tolower); transform(base.begin(), base.end(), base.begin(), ::tolower); @@ -192,7 +202,22 @@ // that it doesn't exist. if (st.st_mtime == 0) return 1; - return st.st_mtime; +#if defined(__APPLE__) && !defined(_POSIX_C_SOURCE) + return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL + + st.st_mtimespec.tv_nsec); +#elif (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \ + defined(__BIONIC__) || (defined (__SVR4) && defined (__sun)) || 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 #endif } diff -Nru ninja-build-1.8.2/src/disk_interface_test.cc ninja-build-1.9.0+om/src/disk_interface_test.cc --- ninja-build-1.8.2/src/disk_interface_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/disk_interface_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -87,6 +87,8 @@ string err; ASSERT_TRUE(disk_.MakeDir("subdir")); ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir")); + EXPECT_GT(disk_.Stat("..", &err), 1); + EXPECT_EQ("", err); EXPECT_GT(disk_.Stat(".", &err), 1); EXPECT_EQ("", err); EXPECT_GT(disk_.Stat("subdir", &err), 1); @@ -105,7 +107,6 @@ #ifdef _WIN32 TEST_F(DiskInterfaceTest, StatCache) { string err; - disk_.AllowStatCache(true); ASSERT_TRUE(Touch("file1")); ASSERT_TRUE(Touch("fiLE2")); @@ -115,6 +116,10 @@ ASSERT_TRUE(Touch("subdir\\SUBFILE2")); ASSERT_TRUE(Touch("subdir\\SUBFILE3")); + disk_.AllowStatCache(false); + TimeStamp parent_stat_uncached = disk_.Stat("..", &err); + disk_.AllowStatCache(true); + EXPECT_GT(disk_.Stat("FIle1", &err), 1); EXPECT_EQ("", err); EXPECT_GT(disk_.Stat("file1", &err), 1); @@ -125,6 +130,8 @@ EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1); EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("..", &err), 1); + EXPECT_EQ("", err); EXPECT_GT(disk_.Stat(".", &err), 1); EXPECT_EQ("", err); EXPECT_GT(disk_.Stat("subdir", &err), 1); @@ -132,11 +139,15 @@ 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); EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err), disk_.Stat("subdir/subsubdir/.", &err)); @@ -202,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 -Nru ninja-build-1.8.2/src/graph.cc ninja-build-1.9.0+om/src/graph.cc --- ninja-build-1.8.2/src/graph.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/graph.cc 2019-01-30 18:58:59.000000000 +0000 @@ -233,7 +233,7 @@ if (output_mtime < most_recent_input->mtime()) { EXPLAIN("%soutput %s older than most recent input %s " - "(%d vs %d)", + "(%" PRId64 " vs %" PRId64 ")", used_restat ? "restat of " : "", output->path().c_str(), most_recent_input->path().c_str(), output_mtime, most_recent_input->mtime()); @@ -257,7 +257,7 @@ // mtime of the most recent input. This can occur even when the mtime // on disk is newer if a previous run wrote to the output file but // exited with an error or was interrupted. - EXPLAIN("recorded mtime of %s older than most recent input %s (%d vs %d)", + EXPLAIN("recorded mtime of %s older than most recent input %s (%" PRId64 " vs %" PRId64 ")", output->path().c_str(), most_recent_input->path().c_str(), entry->mtime, most_recent_input->mtime()); return true; @@ -441,7 +441,7 @@ } void Node::Dump(const char* prefix) const { - printf("%s <%s 0x%p> mtime: %d%s, (:%s), ", + printf("%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ", prefix, path().c_str(), this, mtime(), mtime() ? "" : " (:missing)", dirty() ? " dirty" : " clean"); @@ -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; @@ -547,7 +549,7 @@ // Deps are invalid if the output is newer than the deps. if (output->mtime() > deps->mtime) { - EXPLAIN("stored deps info out of date for '%s' (%d vs %d)", + EXPLAIN("stored deps info out of date for '%s' (%" PRId64 " vs %" PRId64 ")", output->path().c_str(), deps->mtime, output->mtime()); return false; } diff -Nru ninja-build-1.8.2/src/graph.h ninja-build-1.9.0+om/src/graph.h --- ninja-build-1.8.2/src/graph.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/graph.h 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/graph_test.cc ninja-build-1.9.0+om/src/graph_test.cc --- ninja-build-1.8.2/src/graph_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/graph_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/hash_map.h ninja-build-1.9.0+om/src/hash_map.h --- ninja-build-1.8.2/src/hash_map.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/hash_map.h 2019-01-30 18:58:59.000000000 +0000 @@ -18,6 +18,7 @@ #include #include #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 -Nru ninja-build-1.8.2/src/includes_normalize.h ninja-build-1.9.0+om/src/includes_normalize.h --- ninja-build-1.8.2/src/includes_normalize.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/includes_normalize.h 2019-01-30 18:58:59.000000000 +0000 @@ -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& start_list); + const vector& 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 -Nru ninja-build-1.8.2/src/includes_normalize_test.cc ninja-build-1.9.0+om/src/includes_normalize_test.cc --- ninja-build-1.8.2/src/includes_normalize_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/includes_normalize_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/includes_normalize-win32.cc ninja-build-1.9.0+om/src/includes_normalize-win32.cc --- ninja-build-1.8.2/src/includes_normalize-win32.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/includes_normalize-win32.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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]; - GetFullPathName(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL); - GetFullPathName(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]; - GetFullPathName(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& start_list) { - string abs_path = AbsPath(path); + StringPiece path, const vector& start_list, string* err) { + string abs_path = AbsPath(path, err); + if (!err->empty()) + return ""; vector path_list = SplitStringPiece(abs_path, '/'); int i; for (i = 0; i < static_cast(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 -Nru ninja-build-1.8.2/src/lexer.cc ninja-build-1.9.0+om/src/lexer.cc --- ninja-build-1.8.2/src/lexer.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/lexer.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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"); @@ -23,14 +23,14 @@ bool Lexer::Error(const string& message, string* err) { // Compute line/column. int line = 1; - const char* context = input_.str_; + const char* line_start = input_.str_; for (const char* p = input_.str_; p < last_token_; ++p) { if (*p == '\n') { ++line; - context = p + 1; + line_start = p + 1; } } - int col = last_token_ ? (int)(last_token_ - context) : 0; + int col = last_token_ ? (int)(last_token_ - line_start) : 0; char buf[1024]; snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); @@ -43,12 +43,12 @@ int len; bool truncated = true; for (len = 0; len < kTruncateColumn; ++len) { - if (context[len] == 0 || context[len] == '\n') { + if (line_start[len] == 0 || line_start[len] == '\n') { truncated = false; break; } } - *err += string(context, len); + *err += string(line_start, len); if (truncated) *err += "..."; *err += "\n"; @@ -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: + ++p; + { token = NEWLINE; break; } +yy30: 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; + if (yych == '\n') goto yy28; +yy31: + p = q; + if (yyaccept == 0) { + goto yy11; + } else { + goto yy5; + } +yy32: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + yych = *p; +yy33: + if (yybm[0+yych] & 128) { + goto yy32; } - { token = SUBNINJA; break; } + if (yych <= 0x00) goto yy31; + ++p; + { continue; } 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: + 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; { token = PIPE2; break; } -yy45: - yych = *++p; - if (yych != 'f') goto yy27; +yy44: yych = *++p; - if (yych != 'a') goto yy27; + if (yych == 'l') goto yy50; + goto yy14; +yy45: yych = *++p; - if (yych != 'u') goto yy27; + 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 != 'l') goto yy27; + if (yych == 'u') goto yy60; + goto yy14; +yy52: yych = *++p; - if (yych != 't') goto yy27; + if (yych == 'u') goto yy61; + goto yy14; +yy53: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } - { token = DEFAULT; break; } -yy52: - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 'e') goto yy27; + { token = POOL; break; } +yy55: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } { token = RULE; break; } -yy56: - yych = *++p; - if (yych != 'o') goto yy27; +yy57: yych = *++p; - if (yych != 'l') goto yy27; + if (yych == 'i') goto yy62; + goto yy14; +yy58: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } - { token = POOL; break; } + { token = BUILD; break; } yy60: yych = *++p; - if (yych != 'i') goto yy27; + 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 != 'l') goto yy27; + if (yych == 'e') goto yy68; + goto yy14; +yy65: yych = *++p; - if (yych != 'd') goto yy27; + if (yych == 'j') goto yy70; + goto yy14; +yy66: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } - { token = BUILD; break; } -yy65: - ++p; - { token = NEWLINE; break; } -yy67: - ++p; - yych = *p; + { token = DEFAULT; break; } yy68: - if (yybm[0+yych] & 64) { - goto yy67; - } - if (yych >= 0x01) goto yy70; -yy69: - p = q; - if (yyaccept <= 0) { - goto yy3; - } else { - goto yy5; - } -yy70: ++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; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } + { token = INCLUDE; break; } +yy70: yych = *++p; - goto yy8; -yy75: + if (yych != 'a') goto yy14; ++p; - if ((yych = *p) == '\n') goto yy65; - goto yy69; + 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; + if (yybm[0+yych] & 128) { + goto yy79; } + if (yych <= 0x00) goto yy75; + if (yych == '$') goto yy82; + goto yy77; +yy75: ++p; - yych = *p; - goto yy92; -yy79: - { continue; } -yy80: - yych = *(q = ++p); - if (yych == '\n') goto yy85; - if (yych == '\r') goto yy87; -yy81: { break; } -yy82: +yy77: ++p; +yy78: { break; } -yy84: - yych = *++p; - goto yy81; -yy85: +yy79: ++p; + yych = *p; + if (yybm[0+yych] & 128) { + goto yy79; + } { continue; } -yy87: +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 yy89; + if (yych == '\n') goto yy87; p = q; - goto yy81; -yy89: + goto yy78; +yy87: ++p; { continue; } -yy91: - ++p; - yych = *p; -yy92: - if (yybm[0+yych] & 128) { - goto yy91; - } - goto yy79; } } @@ -537,8 +550,9 @@ bool Lexer::ReadIdent(string* out) { const char* p = ofs_; + const char* start; for (;;) { - const char* start = p; + start = p; { unsigned char yych; @@ -577,45 +591,28 @@ 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; + last_token_ = start; + return false; } -yy97: - ++p; - { 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; + } } } + last_token_ = start; ofs_ = p; EatWhitespace(); return true; @@ -631,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; - } + 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 <= '9') { - if (yych == '$') goto yy109; - } else { - if (yych <= ':') goto yy107; - if (yych == '|') goto yy107; - } + if (yych <= ' ') goto yy103; + if (yych <= '$') goto yy107; + goto yy103; } +yy98: ++p; - yych = *p; - goto yy140; -yy104: { - eval->AddText(StringPiece(start, p - start)); - continue; + last_token_ = start; + return Error("unexpected EOF", err); } -yy105: +yy100: ++p; - if ((yych = *p) == '\n') goto yy137; + yych = *p; + if (yybm[0+yych] & 16) { + goto yy100; + } { - last_token_ = start; - return Error(DescribeLastError(), err); + eval->AddText(StringPiece(start, p - start)); + continue; } -yy107: +yy103: ++p; { if (path) { @@ -709,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; } } +yy108: + ++p; + { + if (path) + p = start; + break; + } yy110: ++p; +yy111: { last_token_ = start; - return Error("unexpected EOF", err); + return Error("bad $-escape (literal $ must be written as $$)", err); } yy112: ++p; -yy113: + yych = *p; + if (yybm[0+yych] & 32) { + goto yy112; + } { - last_token_ = start; - return Error("bad $-escape (literal $ must be written as $$)", err); + continue; } -yy114: - yych = *++p; - if (yych == '\n') goto yy134; - goto yy113; yy115: + yych = *++p; + if (yych == '\n') goto yy126; + goto yy111; +yy116: ++p; { eval->AddText(StringPiece(" ", 1)); continue; } -yy117: +yy118: ++p; { eval->AddText(StringPiece("$", 1)); continue; } -yy119: +yy120: ++p; yych = *p; - goto yy133; -yy120: + if (yybm[0+yych] & 64) { + goto yy120; + } { eval->AddSpecial(StringPiece(start + 1, p - start - 1)); continue; } -yy121: +yy123: ++p; { eval->AddText(StringPiece(":", 1)); continue; } -yy123: +yy125: yych = *(q = ++p); - if (yybm[0+yych] & 32) { - goto yy127; + if (yybm[0+yych] & 128) { + goto yy129; } - goto yy113; -yy124: + goto yy111; +yy126: ++p; yych = *p; - if (yybm[0+yych] & 16) { - goto yy124; - } + if (yych == ' ') goto yy126; { continue; } -yy127: +yy129: ++p; yych = *p; - if (yybm[0+yych] & 32) { - goto yy127; + if (yybm[0+yych] & 128) { + goto yy129; } - if (yych == '}') goto yy130; + if (yych == '}') goto yy132; p = q; - goto yy113; -yy130: - ++p; - { - eval->AddSpecial(StringPiece(start + 2, p - start - 3)); - continue; - } + goto yy111; yy132: ++p; - yych = *p; -yy133: - if (yybm[0+yych] & 64) { - goto yy132; - } - goto yy120; -yy134: - ++p; - yych = *p; - if (yych == ' ') goto yy134; { + eval->AddSpecial(StringPiece(start + 2, p - start - 3)); continue; } -yy137: - ++p; - { - if (path) - p = start; - break; - } -yy139: - ++p; - yych = *p; -yy140: - if (yybm[0+yych] & 128) { - goto yy139; - } - goto yy104; } } diff -Nru ninja-build-1.8.2/src/lexer.in.cc ninja-build-1.9.0+om/src/lexer.in.cc --- ninja-build-1.8.2/src/lexer.in.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/lexer.in.cc 2019-01-30 18:58:59.000000000 +0000 @@ -22,14 +22,14 @@ bool Lexer::Error(const string& message, string* err) { // Compute line/column. int line = 1; - const char* context = input_.str_; + const char* line_start = input_.str_; for (const char* p = input_.str_; p < last_token_; ++p) { if (*p == '\n') { ++line; - context = p + 1; + line_start = p + 1; } } - int col = last_token_ ? (int)(last_token_ - context) : 0; + int col = last_token_ ? (int)(last_token_ - line_start) : 0; char buf[1024]; snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); @@ -42,12 +42,12 @@ int len; bool truncated = true; for (len = 0; len < kTruncateColumn; ++len) { - if (context[len] == 0 || context[len] == '\n') { + if (line_start[len] == 0 || line_start[len] == '\n') { truncated = false; break; } } - *err += string(context, len); + *err += string(line_start, len); if (truncated) *err += "..."; *err += "\n"; @@ -182,16 +182,21 @@ bool Lexer::ReadIdent(string* out) { const char* p = ofs_; + const char* start; for (;;) { - const char* start = p; + start = p; /*!re2c varname { out->assign(start, p - start); break; } - [^] { return false; } + [^] { + last_token_ = start; + return false; + } */ } + last_token_ = start; ofs_ = p; EatWhitespace(); return true; diff -Nru ninja-build-1.8.2/src/line_printer.cc ninja-build-1.9.0+om/src/line_printer.cc --- ninja-build-1.8.2/src/line_printer.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/line_printer.cc 2019-01-30 18:58:59.000000000 +0000 @@ -18,6 +18,9 @@ #include #ifdef _WIN32 #include +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 +#endif #else #include #include @@ -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 -Nru ninja-build-1.8.2/src/line_printer.h ninja-build-1.9.0+om/src/line_printer.h --- ninja-build-1.8.2/src/line_printer.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/line_printer.h 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/manifest_parser_test.cc ninja-build-1.9.0+om/src/manifest_parser_test.cc --- ninja-build-1.8.2/src/manifest_parser_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/manifest_parser_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -523,7 +523,7 @@ EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err)); EXPECT_EQ("input:1: unknown build rule 'y'\n" "build x: y z\n" - " ^ near here" + " ^ near here" , err); } @@ -534,7 +534,7 @@ EXPECT_FALSE(parser.ParseTest("build x:: y z\n", &err)); EXPECT_EQ("input:1: expected build command name\n" "build x:: y z\n" - " ^ near here" + " ^ near here" , err); } @@ -636,7 +636,10 @@ string err; EXPECT_FALSE(parser.ParseTest("rule %foo\n", &err)); - EXPECT_EQ("input:1: expected rule name\n", err); + EXPECT_EQ("input:1: expected rule name\n" + "rule %foo\n" + " ^ near here", + err); } { @@ -672,7 +675,10 @@ string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n && bar", &err)); - EXPECT_EQ("input:3: expected variable name\n", err); + EXPECT_EQ("input:3: expected variable name\n" + " && bar\n" + " ^ near here", + err); } { @@ -767,7 +773,9 @@ ManifestParser parser(&local_state, NULL); string err; EXPECT_FALSE(parser.ParseTest("pool\n", &err)); - EXPECT_EQ("input:1: expected pool name\n", err); + EXPECT_EQ("input:1: expected pool name\n" + "pool\n" + " ^ near here", err); } { diff -Nru ninja-build-1.8.2/src/minidump-win32.cc ninja-build-1.9.0+om/src/minidump-win32.cc --- ninja-build-1.8.2/src/minidump-win32.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/minidump-win32.cc 2019-01-30 18:58:59.000000000 +0000 @@ -32,17 +32,17 @@ /// Creates a windows minidump in temp folder. void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep) { char temp_path[MAX_PATH]; - GetTempPath(sizeof(temp_path), temp_path); + GetTempPathA(sizeof(temp_path), temp_path); char temp_file[MAX_PATH]; sprintf(temp_file, "%s\\ninja_crash_dump_%lu.dmp", temp_path, GetCurrentProcessId()); // Delete any previous minidump of the same name. - DeleteFile(temp_file); + DeleteFileA(temp_file); // Load DbgHelp.dll dynamically, as library is not present on all // Windows versions. - HMODULE dbghelp = LoadLibrary("dbghelp.dll"); + HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); if (dbghelp == NULL) { Error("failed to create minidump: LoadLibrary('dbghelp.dll'): %s", GetLastErrorString().c_str()); diff -Nru ninja-build-1.8.2/src/msvc_helper_main-win32.cc ninja-build-1.9.0+om/src/msvc_helper_main-win32.cc --- ninja-build-1.8.2/src/msvc_helper_main-win32.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/msvc_helper_main-win32.cc 2019-01-30 18:58:59.000000000 +0000 @@ -113,7 +113,7 @@ PushPathIntoEnvironment(env); } - char* command = GetCommandLine(); + char* command = GetCommandLineA(); command = strstr(command, " -- "); if (!command) { Fatal("expected command line to end with \" -- command args\""); diff -Nru ninja-build-1.8.2/src/msvc_helper-win32.cc ninja-build-1.9.0+om/src/msvc_helper-win32.cc --- ninja-build-1.8.2/src/msvc_helper-win32.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/msvc_helper-win32.cc 2019-01-30 18:58:59.000000000 +0000 @@ -43,10 +43,10 @@ security_attributes.bInheritHandle = TRUE; // Must be inheritable so subprocesses can dup to children. - HANDLE nul = CreateFile("NUL", GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | - FILE_SHARE_DELETE, - &security_attributes, OPEN_EXISTING, 0, NULL); + HANDLE nul = + CreateFileA("NUL", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, 0, NULL); if (nul == INVALID_HANDLE_VALUE) Fatal("couldn't open nul"); @@ -58,8 +58,8 @@ Win32Fatal("SetHandleInformation"); PROCESS_INFORMATION process_info = {}; - STARTUPINFO startup_info = {}; - startup_info.cb = sizeof(STARTUPINFO); + STARTUPINFOA startup_info = {}; + startup_info.cb = sizeof(STARTUPINFOA); startup_info.hStdInput = nul; startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); startup_info.hStdOutput = stdout_write; diff -Nru ninja-build-1.8.2/src/ninja.cc ninja-build-1.9.0+om/src/ninja.cc --- ninja-build-1.8.2/src/ninja.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/ninja.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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,21 +205,21 @@ "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" -" -k N keep going until N jobs fail [default=1]\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" +" -d MODE enable debugging (use '-d list' to list modes)\n" +" -t TOOL run a subtool (use '-t list' to list subtools)\n" " terminates toplevel options; further flags are passed to the tool\n" -" -w FLAG adjust warnings (use -w list to list warnings)\n", +" -w FLAG adjust warnings (use '-w list' to list warnings)\n", kNinjaVersion, config.parallelism); } @@ -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[]) { @@ -494,7 +503,7 @@ TimeStamp mtime = disk_interface.Stat((*it)->path(), &err); if (mtime == -1) Error("%s", err.c_str()); // Log and ignore Stat() errors; - printf("%s: #deps %d, deps mtime %d (%s)\n", + printf("%s: #deps %d, deps mtime %" PRId64 " (%s)\n", (*it)->path().c_str(), deps->node_count, deps->mtime, (!mtime || mtime > deps->mtime ? "STALE":"VALID")); for (int i = 0; i < deps->node_count; ++i) @@ -662,7 +671,65 @@ } } -int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* argv[]) { +enum EvaluateCommandMode { + ECM_NORMAL, + ECM_EXPAND_RSPFILE +}; +string EvaluateCommandWithRspfile(Edge* edge, EvaluateCommandMode mode) { + string command = edge->EvaluateCommand(); + if (mode == ECM_NORMAL) + return command; + + string rspfile = edge->GetUnescapedRspfile(); + if (rspfile.empty()) + return command; + + size_t index = command.find(rspfile); + if (index == 0 || index == string::npos || command[index - 1] != '@') + return command; + + string rspfile_content = edge->GetBinding("rspfile_content"); + size_t newline_index = 0; + while ((newline_index = rspfile_content.find('\n', newline_index)) != + string::npos) { + rspfile_content.replace(newline_index, 1, 1, ' '); + ++newline_index; + } + command.replace(index - 1, rspfile.length() + 1, rspfile_content); + return command; +} + +int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, + char* argv[]) { + // The compdb tool uses getopt, and expects argv[0] to contain the name of + // the tool, i.e. "compdb". + argc++; + argv--; + + EvaluateCommandMode eval_mode = ECM_NORMAL; + + optind = 1; + int opt; + while ((opt = getopt(argc, argv, const_cast("hx"))) != -1) { + switch(opt) { + case 'x': + eval_mode = ECM_EXPAND_RSPFILE; + break; + + case 'h': + default: + printf( + "usage: ninja -t compdb [options] [rules]\n" + "\n" + "options:\n" + " -x expand @rspfile style response file invocations\n" + ); + return 1; + } + } + argv += optind; + argc -= optind; + bool first = true; vector cwd; @@ -688,9 +755,11 @@ printf("\n {\n \"directory\": \""); EncodeJSONString(&cwd[0]); printf("\",\n \"command\": \""); - EncodeJSONString((*e)->EvaluateCommand().c_str()); + EncodeJSONString(EvaluateCommandWithRspfile(*e, eval_mode).c_str()); printf("\",\n \"file\": \""); EncodeJSONString((*e)->inputs_[0]->path().c_str()); + printf("\",\n \"output\": \""); + EncodeJSONString((*e)->outputs_[0]->path().c_str()); printf("\"\n }"); first = false; @@ -743,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 }, @@ -849,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; @@ -863,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", @@ -1042,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 } }; @@ -1060,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': { @@ -1118,17 +1197,25 @@ return -1; } -int real_main(int argc, char** argv) { +NORETURN void real_main(int argc, char** argv) { + // Use exit() instead of return in this function to avoid potentially + // expensive cleanup when destructing NinjaMain. BuildConfig config; Options options = {}; options.input_file = "build.ninja"; + options.dupe_edges_should_err = true; setvbuf(stdout, NULL, _IOLBF, BUFSIZ); const char* ninja_command = argv[0]; int exit_code = ReadFlags(&argc, &argv, &options, &config); if (exit_code >= 0) - return exit_code; + exit(exit_code); + + if (options.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 @@ -1147,7 +1234,7 @@ // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed // by other tools. NinjaMain ninja(ninja_command, config); - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); } // Limit number of rebuilds, to prevent infinite loops. @@ -1166,43 +1253,43 @@ string err; if (!parser.Load(options.input_file, &err)) { Error("%s", err.c_str()); - return 1; + exit(1); } if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD) - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); if (!ninja.EnsureBuildDirExists()) - return 1; + exit(1); if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog()) - return 1; + exit(1); if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS) - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); // Attempt to rebuild the manifest before building anything else if (ninja.RebuildManifest(options.input_file, &err)) { // In dry_run mode the regeneration will succeed without changing the // manifest forever. Better to return immediately. if (config.dry_run) - return 0; + exit(0); // Start the build over with the new manifest. continue; } else if (!err.empty()) { Error("rebuilding '%s': %s", options.input_file, err.c_str()); - return 1; + exit(1); } int result = ninja.RunBuild(argc, argv); if (g_metrics) ninja.DumpMetrics(); - return result; + exit(result); } Error("manifest '%s' still dirty after %d tries\n", options.input_file, kCycleLimit); - return 1; + exit(1); } } // anonymous namespace @@ -1215,7 +1302,7 @@ __try { // Running inside __try ... __except suppresses any Windows error // dialogs for errors such as bad_alloc. - return real_main(argc, argv); + real_main(argc, argv); } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { // Common error situations return exitCode=1. 2 was chosen to @@ -1223,6 +1310,6 @@ return 2; } #else - return real_main(argc, argv); + real_main(argc, argv); #endif } diff -Nru ninja-build-1.8.2/src/state.h ninja-build-1.9.0+om/src/state.h --- ninja-build-1.8.2/src/state.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/state.h 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/subprocess-posix.cc ninja-build-1.9.0+om/src/subprocess-posix.cc --- ninja-build-1.8.2/src/subprocess-posix.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/subprocess-posix.cc 2019-01-30 18:58:59.000000000 +0000 @@ -14,6 +14,7 @@ #include "subprocess.h" +#include #include #include #include @@ -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)); - - if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0) - Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno)); + int err = posix_spawn_file_actions_init(&action); + if (err != 0) + Fatal("posix_spawn_file_actions_init: %s", strerror(err)); + + 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(spawned_args), environ) != 0) - Fatal("posix_spawn: %s", strerror(errno)); - - 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_spawn(&pid_, "/bin/sh", &action, &attr, + const_cast(spawned_args), environ); + if (err != 0) + Fatal("posix_spawn: %s", strerror(err)); + + 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 -Nru ninja-build-1.8.2/src/subprocess_test.cc ninja-build-1.9.0+om/src/subprocess_test.cc --- ninja-build-1.8.2/src/subprocess_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/subprocess_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -182,7 +182,7 @@ "cmd /c echo hi", "cmd /c time /t", #else - "whoami", + "id -u", "pwd", #endif }; diff -Nru ninja-build-1.8.2/src/subprocess-win32.cc ninja-build-1.9.0+om/src/subprocess-win32.cc --- ninja-build-1.8.2/src/subprocess-win32.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/subprocess-win32.cc 2019-01-30 18:58:59.000000000 +0000 @@ -59,8 +59,8 @@ } // Get the write end of the pipe as a handle inheritable across processes. - HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, 0, NULL); + HANDLE output_write_handle = + CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); HANDLE output_write_child; if (!DuplicateHandle(GetCurrentProcess(), output_write_handle, GetCurrentProcess(), &output_write_child, @@ -80,9 +80,10 @@ security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); security_attributes.bInheritHandle = TRUE; // Must be inheritable so subprocesses can dup to children. - HANDLE nul = CreateFile("NUL", GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - &security_attributes, OPEN_EXISTING, 0, NULL); + HANDLE nul = + CreateFileA("NUL", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, 0, NULL); if (nul == INVALID_HANDLE_VALUE) Fatal("couldn't open nul"); @@ -123,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 -Nru ninja-build-1.8.2/src/test.h ninja-build-1.9.0+om/src/test.h --- ninja-build-1.8.2/src/test.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/test.h 2019-01-30 18:58:59.000000000 +0000 @@ -104,7 +104,7 @@ } \ } -// Support utilites for tests. +// Support utilities for tests. struct Node; diff -Nru ninja-build-1.8.2/src/timestamp.h ninja-build-1.9.0+om/src/timestamp.h --- ninja-build-1.8.2/src/timestamp.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/timestamp.h 2019-01-30 18:58:59.000000000 +0000 @@ -15,10 +15,19 @@ #ifndef NINJA_TIMESTAMP_H_ #define NINJA_TIMESTAMP_H_ +#ifdef _WIN32 +#include "win32port.h" +#else +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include +#endif + // When considering file modification times we only care to compare // them against one another -- we never convert them to an absolute -// real time. On POSIX we use time_t (seconds since epoch) and on -// Windows we use a different value. Both fit in an int. -typedef int TimeStamp; +// real time. On POSIX we use timespec (seconds&nanoseconds since epoch) +// and on Windows we use a different value. Both fit in an int64. +typedef int64_t TimeStamp; #endif // NINJA_TIMESTAMP_H_ diff -Nru ninja-build-1.8.2/src/util.cc ninja-build-1.9.0+om/src/util.cc --- ninja-build-1.8.2/src/util.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/util.cc 2019-01-30 18:58:59.000000000 +0000 @@ -197,7 +197,7 @@ case '\\': bits |= bits_mask; *c = '/'; - // Intentional fallthrough. + NINJA_FALLTHROUGH; case '/': bits_mask <<= 1; } @@ -318,13 +318,8 @@ // This makes a ninja run on a set of 1500 manifest files about 4% faster // than using the generic fopen code below. err->clear(); - HANDLE f = ::CreateFile(path.c_str(), - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - NULL); + HANDLE f = ::CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (f == INVALID_HANDLE_VALUE) { err->assign(GetLastErrorString()); return -ENOENT; @@ -351,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)) { @@ -437,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 @@ -578,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 -Nru ninja-build-1.8.2/src/util.h ninja-build-1.9.0+om/src/util.h --- ninja-build-1.8.2/src/util.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/util.h 2019-01-30 18:58:59.000000000 +0000 @@ -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 -Nru ninja-build-1.8.2/src/util_test.cc ninja-build-1.9.0+om/src/util_test.cc --- ninja-build-1.8.2/src/util_test.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/util_test.cc 2019-01-30 18:58:59.000000000 +0000 @@ -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)); } diff -Nru ninja-build-1.8.2/src/version.cc ninja-build-1.9.0+om/src/version.cc --- ninja-build-1.8.2/src/version.cc 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/version.cc 2019-01-30 18:58:59.000000000 +0000 @@ -18,7 +18,7 @@ #include "util.h" -const char* kNinjaVersion = "1.8.2"; +const char* kNinjaVersion = "1.9.0"; void ParseVersion(const string& version, int* major, int* minor) { size_t end = version.find('.'); diff -Nru ninja-build-1.8.2/src/win32port.h ninja-build-1.9.0+om/src/win32port.h --- ninja-build-1.8.2/src/win32port.h 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/src/win32port.h 2019-01-30 18:58:59.000000000 +0000 @@ -15,6 +15,13 @@ #ifndef NINJA_WIN32PORT_H_ #define NINJA_WIN32PORT_H_ +#if defined(__MINGW32__) || defined(__MINGW64__) +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include +#endif + typedef signed short int16_t; typedef unsigned short uint16_t; /// A 64-bit integer type @@ -23,6 +30,7 @@ // printf format specifier for uint64_t, from C99. #ifndef PRIu64 +#define PRId64 "I64d" #define PRIu64 "I64u" #define PRIx64 "I64x" #endif diff -Nru ninja-build-1.8.2/.travis.yml ninja-build-1.9.0+om/.travis.yml --- ninja-build-1.8.2/.travis.yml 2017-09-11 01:20:10.000000000 +0000 +++ ninja-build-1.9.0+om/.travis.yml 2019-01-30 18:58:59.000000000 +0000 @@ -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