├── .clang-format ├── .clang-tidy ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── linux-musl.yml │ ├── linux.yml │ ├── macos.yml │ └── windows.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING.md ├── COPYING ├── README.md ├── RELEASING.md ├── appveyor.yml ├── configure.py ├── doc ├── README.md ├── dblatex.xsl ├── docbook.xsl ├── doxygen.config ├── manual.asciidoc └── style.css ├── misc ├── afl-fuzz-tokens │ ├── kw_build │ ├── kw_default │ ├── kw_include │ ├── kw_pool │ ├── kw_rule │ ├── kw_subninja │ ├── misc_a │ ├── misc_b │ ├── misc_colon │ ├── misc_cont │ ├── misc_dollar │ ├── misc_eq │ ├── misc_indent │ ├── misc_pipe │ ├── misc_pipepipe │ └── misc_space ├── afl-fuzz │ └── build.ninja ├── bash-completion ├── ci.py ├── inherited-fds.ninja ├── jobserver_pool.py ├── jobserver_pool_test.py ├── jobserver_test.py ├── jobserver_test_helper.py ├── long-slow-build.ninja ├── manifest_fuzzer.cc ├── measure.py ├── ninja.vim ├── ninja_syntax.py ├── ninja_syntax_test.py ├── oss-fuzz │ ├── build.sh │ └── sample_ninja_build ├── output_test.py ├── packaging │ ├── ninja.spec │ └── rpmbuild.sh ├── write_fake_manifests.py └── zsh-completion ├── src ├── browse.cc ├── browse.h ├── browse.py ├── build.cc ├── build.h ├── build_log.cc ├── build_log.h ├── build_log_perftest.cc ├── build_log_test.cc ├── build_test.cc ├── canon_perftest.cc ├── clean.cc ├── clean.h ├── clean_test.cc ├── clparser.cc ├── clparser.h ├── clparser_perftest.cc ├── clparser_test.cc ├── command_collector.h ├── debug_flags.cc ├── debug_flags.h ├── depfile_parser.cc ├── depfile_parser.h ├── depfile_parser.in.cc ├── depfile_parser_perftest.cc ├── depfile_parser_test.cc ├── deps_log.cc ├── deps_log.h ├── deps_log_test.cc ├── disk_interface.cc ├── disk_interface.h ├── disk_interface_test.cc ├── dyndep.cc ├── dyndep.h ├── dyndep_parser.cc ├── dyndep_parser.h ├── dyndep_parser_test.cc ├── edit_distance.cc ├── edit_distance.h ├── edit_distance_test.cc ├── elide_middle.cc ├── elide_middle.h ├── elide_middle_perftest.cc ├── elide_middle_test.cc ├── eval_env.cc ├── eval_env.h ├── exit_status.h ├── explanations.h ├── explanations_test.cc ├── gen_doxygen_mainpage.sh ├── getopt.c ├── getopt.h ├── graph.cc ├── graph.h ├── graph_test.cc ├── graphviz.cc ├── graphviz.h ├── hash_collision_bench.cc ├── hash_map.h ├── includes_normalize-win32.cc ├── includes_normalize.h ├── includes_normalize_test.cc ├── inline.sh ├── jobserver-posix.cc ├── jobserver-win32.cc ├── jobserver.cc ├── jobserver.h ├── jobserver_test.cc ├── json.cc ├── json.h ├── json_test.cc ├── lexer.cc ├── lexer.h ├── lexer.in.cc ├── lexer_test.cc ├── line_printer.cc ├── line_printer.h ├── load_status.h ├── manifest_parser.cc ├── manifest_parser.h ├── manifest_parser_perftest.cc ├── manifest_parser_test.cc ├── metrics.cc ├── metrics.h ├── minidump-win32.cc ├── missing_deps.cc ├── missing_deps.h ├── missing_deps_test.cc ├── msvc_helper-win32.cc ├── msvc_helper.h ├── msvc_helper_main-win32.cc ├── msvc_helper_test.cc ├── ninja.cc ├── ninja_test.cc ├── parser.cc ├── parser.h ├── real_command_runner.cc ├── state.cc ├── state.h ├── state_test.cc ├── status.h ├── status_printer.cc ├── status_printer.h ├── status_test.cc ├── string_piece.h ├── string_piece_util.cc ├── string_piece_util.h ├── string_piece_util_test.cc ├── subprocess-posix.cc ├── subprocess-win32.cc ├── subprocess.h ├── subprocess_test.cc ├── test.cc ├── test.h ├── third_party │ ├── emhash │ │ ├── README.ninja │ │ └── hash_table8.hpp │ └── rapidhash │ │ ├── README.ninja │ │ └── rapidhash.h ├── timestamp.h ├── util.cc ├── util.h ├── util_test.cc ├── version.cc ├── version.h └── win32port.h └── windows └── ninja.manifest /.clang-format: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This isn't meant to be authoritative, but it's good enough to be useful. 16 | # Still use your best judgement for formatting decisions: clang-format 17 | # sometimes makes strange choices. 18 | 19 | BasedOnStyle: Google 20 | AllowShortFunctionsOnASingleLine: Inline 21 | AllowShortIfStatementsOnASingleLine: false 22 | AllowShortLoopsOnASingleLine: false 23 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 24 | Cpp11BracedListStyle: false 25 | IndentCaseLabels: false 26 | DerivePointerBinding: false 27 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: ' 3 | ,readability-avoid-const-params-in-decls, 4 | ,readability-inconsistent-declaration-parameter-name, 5 | ,readability-non-const-parameter, 6 | ,readability-redundant-string-cstr, 7 | ,readability-redundant-string-init, 8 | ,readability-simplify-boolean-expr, 9 | ,cppcoreguidelines-pro-type-cstyle-cast, 10 | ' 11 | WarningsAsErrors: ' 12 | ,readability-avoid-const-params-in-decls, 13 | ,readability-inconsistent-declaration-parameter-name, 14 | ,readability-non-const-parameter, 15 | ,readability-redundant-string-cstr, 16 | ,readability-redundant-string-init, 17 | ,readability-simplify-boolean-expr, 18 | ,cppcoreguidelines-pro-type-cstyle-cast, 19 | ' 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | end_of_line = lf 9 | 10 | [CMakeLists.txt] 11 | indent_style = tab 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/workflows/linux-musl.yml: -------------------------------------------------------------------------------- 1 | name: ci-linux-musl 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | release: 8 | types: [published] 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | permissions: {} 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-24.04 19 | container: alpine:edge 20 | permissions: 21 | contents: read 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | build_method: ["python", "cmake"] 26 | 27 | steps: 28 | - name: Host - checkout 29 | uses: actions/checkout@v4 30 | with: 31 | fetch-depth: 0 32 | persist-credentials: false 33 | 34 | - name: Install ninja build optional dependencies 35 | run: apk update && apk add -u --no-cache python3 build-base cmake re2c 36 | 37 | - name: Configure ninja build 38 | if: matrix.build_method == 'cmake' 39 | run: cmake -B build -D CMAKE_BUILD_TYPE="Release" 40 | 41 | - name: Cmake Build ninja 42 | if: matrix.build_method == 'cmake' 43 | run: cmake --build build --parallel --config Release 44 | 45 | - name: Cmake test ninja 46 | if: matrix.build_method == 'cmake' 47 | run: build/ninja_test --gtest_color=yes 48 | 49 | - name: Python Build ninja 50 | if: matrix.build_method == 'python' 51 | run: python3 configure.py --bootstrap --verbose 52 | 53 | - name: Python test ninja 54 | if: matrix.build_method == 'python' 55 | run: | 56 | ./ninja all 57 | python3 misc/ninja_syntax_test.py 58 | # python3 misc/output_test.py 59 | 60 | - name: Move ninja binary 61 | if: matrix.build_method == 'cmake' 62 | run: mv -f build/ninja ninja 63 | 64 | - name: ninja-ninja --version 65 | run: ./ninja --version >> $GITHUB_STEP_SUMMARY 66 | 67 | - name: binary info via file 68 | run: file ./ninja >> $GITHUB_STEP_SUMMARY 69 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macOS 2 | 3 | on: 4 | pull_request: 5 | push: 6 | release: 7 | types: published 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-13 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Install dependencies 17 | run: brew install re2c p7zip cmake 18 | 19 | - name: Build ninja 20 | shell: bash 21 | env: 22 | MACOSX_DEPLOYMENT_TARGET: 10.15 23 | run: | 24 | cmake -Bbuild -GXcode '-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64' 25 | cmake --build build --config Release 26 | 27 | - name: Test ninja (Release) 28 | run: ./ninja_test 29 | working-directory: build/Release 30 | 31 | - name: Create ninja archive 32 | shell: bash 33 | run: | 34 | mkdir artifact 35 | 7z a artifact/ninja-mac.zip ./build/Release/ninja 36 | 37 | # Upload ninja binary archive as an artifact 38 | - name: Upload artifact 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: ninja-binary-archives 42 | path: artifact 43 | 44 | - name: Upload release asset 45 | if: github.event.action == 'published' 46 | uses: actions/upload-release-asset@v1 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | with: 50 | upload_url: ${{ github.event.release.upload_url }} 51 | asset_path: ./artifact/ninja-mac.zip 52 | asset_name: ninja-mac.zip 53 | asset_content_type: application/zip 54 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | pull_request: 5 | push: 6 | release: 7 | types: published 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - arch: 'x64' 18 | suffix: '' 19 | - arch: 'arm64' 20 | suffix: 'arm64' 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Install dependencies 26 | run: choco install re2c 27 | 28 | - name: Build ninja 29 | shell: bash 30 | run: | 31 | cmake -Bbuild -A ${{ matrix.arch }} 32 | cmake --build build --parallel --config Debug 33 | cmake --build build --parallel --config Release 34 | 35 | - name: Test ninja (Debug) 36 | if: matrix.arch != 'arm64' 37 | run: .\ninja_test.exe 38 | working-directory: build/Debug 39 | 40 | - name: Test ninja (Release) 41 | if: matrix.arch != 'arm64' 42 | run: .\ninja_test.exe 43 | working-directory: build/Release 44 | 45 | - name: Create ninja archive 46 | shell: bash 47 | run: | 48 | mkdir artifact 49 | 7z a artifact/ninja-win${{ matrix.suffix }}.zip ./build/Release/ninja.exe 50 | 51 | # Upload ninja binary archive as an artifact 52 | - name: Upload artifact 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: ninja-binary-archives${{ matrix.suffix }} 56 | path: artifact 57 | 58 | - name: Upload release asset 59 | if: github.event.action == 'published' 60 | uses: actions/upload-release-asset@v1 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | with: 64 | upload_url: ${{ github.event.release.upload_url }} 65 | asset_path: ./artifact/ninja-win${{ matrix.suffix }}.zip 66 | asset_name: ninja-win${{ matrix.suffix }}.zip 67 | asset_content_type: application/zip 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.obj 3 | *.exe 4 | *.pdb 5 | *.ilk 6 | /build*/ 7 | /build.ninja 8 | /ninja 9 | /ninja.bootstrap 10 | /build_log_perftest 11 | /canon_perftest 12 | /clparser_perftest 13 | /elide_middle_perftest 14 | /depfile_parser_perftest 15 | /hash_collision_bench 16 | /ninja_test 17 | /manifest_parser_perftest 18 | /graph.png 19 | /doc/manual.html 20 | /doc/doxygen 21 | *.patch 22 | .DS_Store 23 | 24 | # Eclipse project files 25 | .project 26 | .cproject 27 | 28 | # SublimeText project files 29 | *.sublime-project 30 | *.sublime-workspace 31 | 32 | # Ninja output 33 | .ninja_deps 34 | .ninja_log 35 | 36 | # Visual Studio Code project files 37 | /.vscode/ 38 | /.ccls-cache/ 39 | 40 | # Qt Creator project files 41 | /CMakeLists.txt.user 42 | 43 | # clangd 44 | /.clangd/ 45 | /compile_commands.json 46 | /.cache/ 47 | 48 | # Visual Studio files 49 | /.vs/ 50 | /out/ 51 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to successfully make changes to Ninja 2 | 3 | We're very wary of changes that increase the complexity of Ninja (in particular, 4 | new build file syntax or command-line flags) or increase the maintenance burden 5 | of Ninja. Ninja is already successfully used by hundreds of developers for large 6 | projects and it already achieves (most of) the goals we set out for it to do. 7 | It's probably best to discuss new feature ideas on the 8 | [mailing list](https://groups.google.com/forum/#!forum/ninja-build) or in an 9 | issue before creating a PR. 10 | 11 | ## Coding guidelines 12 | 13 | Generally it's the 14 | [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) with 15 | a few additions: 16 | 17 | * We have used `using namespace std;` a lot in the past. For new contributions, 18 | please try to avoid relying on it and instead whenever possible use `std::`. 19 | However, please do not change existing code simply to add `std::` unless your 20 | contribution already needs to change that line of code anyway. 21 | * Use `///` for [Doxygen](http://www.doxygen.nl/) (use `\a` to refer to 22 | arguments). 23 | * It's not necessary to document each argument, especially when they're 24 | relatively self-evident (e.g. in 25 | `CanonicalizePath(string* path, string* err)`, the arguments are hopefully 26 | obvious). 27 | 28 | If you're unsure about code formatting, please use 29 | [clang-format](https://clang.llvm.org/docs/ClangFormat.html). However, please do 30 | not format code that is not otherwise part of your contribution. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ninja 2 | 3 | Ninja is a small build system with a focus on speed. 4 | https://ninja-build.org/ 5 | 6 | See [the manual](https://ninja-build.org/manual.html) or 7 | `doc/manual.asciidoc` included in the distribution for background 8 | and more details. 9 | 10 | Binaries for Linux, Mac and Windows are available on 11 | [GitHub](https://github.com/ninja-build/ninja/releases). 12 | Run `./ninja -h` for Ninja help. 13 | 14 | Installation is not necessary because the only required file is the 15 | resulting ninja binary. However, to enable features like Bash 16 | completion and Emacs and Vim editing modes, some files in misc/ must be 17 | copied to appropriate locations. 18 | 19 | If you're interested in making changes to Ninja, read 20 | [CONTRIBUTING.md](CONTRIBUTING.md) first. 21 | 22 | ## Building Ninja itself 23 | 24 | You can either build Ninja via the custom generator script written in Python or 25 | via CMake. For more details see 26 | [the wiki](https://github.com/ninja-build/ninja/wiki). 27 | 28 | ### Python 29 | 30 | ``` 31 | ./configure.py --bootstrap 32 | ``` 33 | 34 | This will generate the `ninja` binary and a `build.ninja` file you can now use 35 | to build Ninja with itself. 36 | 37 | If you have a GoogleTest source directory, you can build the tests 38 | by passing its path with `--gtest-source-dir=PATH` option, or the 39 | `GTEST_SOURCE_DIR` environment variable, e.g.: 40 | 41 | ``` 42 | ./configure.py --bootstrap --gtest-source-dir=/path/to/googletest 43 | ./ninja all # build ninja_test and other auxiliary binaries 44 | ./ninja_test` # run the unit-test suite. 45 | ``` 46 | 47 | Use the CMake build below if you want to use a preinstalled binary 48 | version of the library. 49 | 50 | ### CMake 51 | 52 | To build the ninja binary without building the unit tests, disable test building by setting `BUILD_TESTING` to `OFF`: 53 | 54 | ``` 55 | cmake -Bbuild-cmake -DBUILD_TESTING=OFF 56 | cmake --build build-cmake 57 | ``` 58 | 59 | The `ninja` binary will now be inside the `build-cmake` directory (you can 60 | choose any other name you like). 61 | 62 | To run the unit tests, omit the `-DBUILD_TESTING=OFF` option, and after building, run: 63 | 64 | ``` 65 | ./build-cmake/ninja_test 66 | ``` 67 | 68 | ## Generating documentation 69 | 70 | ### Ninja Manual 71 | 72 | You must have `asciidoc` and `xsltproc` in your PATH, then do: 73 | 74 | ``` 75 | ./configure.py 76 | ninja manual doc/manual.html 77 | ``` 78 | 79 | Which will generate `doc/manual.html`. 80 | 81 | To generate the PDF version of the manual, you must have `dblatext` in your PATH then do: 82 | 83 | ``` 84 | ./configure.py # only if you didn't do it previously. 85 | ninja doc/manual.pdf 86 | ``` 87 | 88 | Which will generate `doc/manual.pdf`. 89 | 90 | ### Doxygen documentation 91 | 92 | If you have `doxygen` installed, you can build documentation extracted from C++ 93 | declarations and comments to help you navigate the code. Note that Ninja is a standalone 94 | executable, not a library, so there is no public API, all details exposed here are 95 | internal. 96 | 97 | ``` 98 | ./configure.py # if needed 99 | ninja doxygen 100 | ``` 101 | 102 | Then open `doc/doxygen/html/index.html` in a browser to look at it. 103 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Notes to myself on all the steps to make for a Ninja release. 2 | 3 | ### Push new release branch: 4 | 1. Run afl-fuzz for a day or so and run ninja_test 5 | 2. Consider sending a heads-up to the ninja-build mailing list first 6 | 3. Make sure branches 'master' and 'release' are synced up locally 7 | 4. Update src/version.cc with new version (with ".git"), then 8 | ``` 9 | git commit -am 'mark this 1.5.0.git' 10 | ``` 11 | 5. git checkout release; git merge master 12 | 6. Fix version number in src/version.cc (it will likely conflict in the above) 13 | 7. Fix version in doc/manual.asciidoc (exists only on release branch) 14 | 8. commit, tag, push (don't forget to push --tags) 15 | ``` 16 | git commit -am v1.5.0; git push origin release 17 | git tag v1.5.0; git push --tags 18 | # Push the 1.5.0.git change on master too: 19 | git checkout master; git push origin master 20 | ``` 21 | 9. Construct release notes from prior notes 22 | 23 | credits: `git shortlog -s --no-merges REV..` 24 | 25 | 26 | ### Release on GitHub: 27 | 1. Go to [Tags](https://github.com/ninja-build/ninja/tags) 28 | 2. Open the newly created tag and select "Create release from tag" 29 | 3. Create the release which will trigger a build which automatically attaches 30 | the binaries 31 | 32 | ### Make announcement on mailing list: 33 | 1. copy old mail 34 | 35 | ### Update website: 36 | 1. Make sure your ninja checkout is on the v1.5.0 tag 37 | 2. Clone https://github.com/ninja-build/ninja-build.github.io 38 | 3. In that repo, `./update-docs.sh` 39 | 4. Update index.html with newest version and link to release notes 40 | 5. `git commit -m 'run update-docs.sh, 1.5.0 release'` 41 | 6. `git push origin master` 42 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: 3 | - Visual Studio 2017 4 | - Ubuntu2204 5 | 6 | environment: 7 | CLICOLOR_FORCE: 1 8 | CHERE_INVOKING: 1 # Tell Bash to inherit the current working directory 9 | matrix: 10 | - MSYSTEM: MINGW64 11 | - MSYSTEM: LINUX 12 | 13 | matrix: 14 | exclude: 15 | - image: Visual Studio 2017 16 | MSYSTEM: LINUX 17 | - image: Ubuntu2204 18 | MSYSTEM: MINGW64 19 | 20 | for: 21 | - 22 | matrix: 23 | only: 24 | - MSYSTEM: MINGW64 25 | build_script: 26 | ps: "C:\\msys64\\usr\\bin\\bash -lc @\"\n 27 | pacman -S --quiet --noconfirm --needed re2c 2>&1\n 28 | ./configure.py --bootstrap --platform mingw 2>&1\n 29 | ./ninja all\n 30 | ./misc/ninja_syntax_test.py 2>&1\n\"@" 31 | - matrix: 32 | only: 33 | - image: Ubuntu2204 34 | build_script: 35 | - ./configure.py --bootstrap 36 | - ./ninja all 37 | - misc/ninja_syntax_test.py 38 | - misc/output_test.py 39 | 40 | test: off 41 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the Ninja manual and support files used in 2 | building it. Here's a brief overview of how it works. 3 | 4 | The source text, `manual.asciidoc`, is written in the AsciiDoc format. 5 | AsciiDoc can generate HTML but it doesn't look great; instead, we use 6 | AsciiDoc to generate the Docbook XML format and then provide our own 7 | Docbook XSL tweaks to produce HTML from that. 8 | 9 | In theory using AsciiDoc and DocBook allows us to produce nice PDF 10 | documentation etc. In reality it's not clear anyone wants that, but the 11 | build rules are in place to generate it if you install dblatex. 12 | -------------------------------------------------------------------------------- /doc/dblatex.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 0 6 | 0 7 | 8 | -------------------------------------------------------------------------------- /doc/docbook.xsl: -------------------------------------------------------------------------------- 1 | 3 | 5 | ]> 6 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 19 | book toc 20 | 21 | 22 | 0 23 | 24 | 25 | 1 26 | 27 | 30 | ul 31 | 32 | 34 | 35 | -------------------------------------------------------------------------------- /doc/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | color-scheme: light dark; 3 | } 4 | 5 | body { 6 | margin: 5ex 10ex; 7 | max-width: 80ex; 8 | line-height: 1.5; 9 | font-family: sans-serif; 10 | } 11 | 12 | h1, h2, h3 { 13 | font-weight: normal; 14 | } 15 | 16 | pre, code { 17 | font-family: x, monospace; 18 | } 19 | 20 | pre { 21 | padding: 1ex; 22 | background: #eee; 23 | border: solid 1px #ddd; 24 | min-width: 0; 25 | font-size: 90%; 26 | } 27 | @media (prefers-color-scheme: dark) { 28 | pre { 29 | background: #333; 30 | border: solid 1px #444; 31 | } 32 | } 33 | 34 | code { 35 | color: #007; 36 | } 37 | @media (prefers-color-scheme: dark) { 38 | code { 39 | color: #a7cec8; 40 | } 41 | } 42 | 43 | div.chapter { 44 | margin-top: 4em; 45 | border-top: solid 2px black; 46 | } 47 | @media (prefers-color-scheme: dark) { 48 | div.chapter { 49 | border-top: solid 2px white; 50 | } 51 | } 52 | 53 | p { 54 | margin-top: 0; 55 | } 56 | 57 | /* The following applies to the left column of a [horizontal] labeled list: */ 58 | table.horizontal > tbody > tr > td:nth-child(1) { 59 | 60 | /* prevent the insertion of a line-break in the middle of a label: */ 61 | white-space: nowrap; 62 | 63 | /* insert a little horizontal padding between the two columns: */ 64 | padding-right: 1.5em; 65 | 66 | /* right-justify labels: */ 67 | text-align: end; 68 | } 69 | -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/kw_build: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/kw_default: -------------------------------------------------------------------------------- 1 | default -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/kw_include: -------------------------------------------------------------------------------- 1 | include -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/kw_pool: -------------------------------------------------------------------------------- 1 | pool -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/kw_rule: -------------------------------------------------------------------------------- 1 | rule -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/kw_subninja: -------------------------------------------------------------------------------- 1 | subninja -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_a: -------------------------------------------------------------------------------- 1 | a -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_b: -------------------------------------------------------------------------------- 1 | b -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_colon: -------------------------------------------------------------------------------- 1 | : -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_cont: -------------------------------------------------------------------------------- 1 | $ 2 | -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_dollar: -------------------------------------------------------------------------------- 1 | $ -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_eq: -------------------------------------------------------------------------------- 1 | = -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_indent: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_pipe: -------------------------------------------------------------------------------- 1 | | -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_pipepipe: -------------------------------------------------------------------------------- 1 | || -------------------------------------------------------------------------------- /misc/afl-fuzz-tokens/misc_space: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /misc/afl-fuzz/build.ninja: -------------------------------------------------------------------------------- 1 | rule b 2 | command = clang -MMD -MF $out.d -o $out -c $in 3 | description = building $out 4 | 5 | build a.o: b a.c 6 | -------------------------------------------------------------------------------- /misc/bash-completion: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Add the following to your .bashrc to tab-complete ninja targets 16 | # . path/to/ninja/misc/bash-completion 17 | 18 | _ninja_target() { 19 | local cur prev targets dir line targets_command OPTIND 20 | 21 | # When available, use bash_completion to: 22 | # 1) Complete words when the cursor is in the middle of the word 23 | # 2) Complete paths with files or directories, as appropriate 24 | if _get_comp_words_by_ref cur prev &>/dev/null ; then 25 | case $prev in 26 | -f) 27 | _filedir 28 | return 0 29 | ;; 30 | -C) 31 | _filedir -d 32 | return 0 33 | ;; 34 | esac 35 | else 36 | cur="${COMP_WORDS[COMP_CWORD]}" 37 | fi 38 | 39 | if [[ "$cur" == "--"* ]]; then 40 | # there is currently only one argument that takes -- 41 | COMPREPLY=($(compgen -P '--' -W 'version' -- "${cur:2}")) 42 | else 43 | dir="." 44 | line=$(echo ${COMP_LINE} | cut -d" " -f 2-) 45 | # filter out all non relevant arguments but keep C for dirs 46 | while getopts :C:f:j:l:k:nvd:t: opt $line; do 47 | case $opt in 48 | # eval for tilde expansion 49 | C) eval dir="$OPTARG" ;; 50 | esac 51 | done; 52 | targets_command="eval ninja -C \"${dir}\" -t targets all 2>/dev/null | cut -d: -f1" 53 | COMPREPLY=($(compgen -W '`${targets_command}`' -- "$cur")) 54 | fi 55 | return 56 | } 57 | complete -F _ninja_target ninja 58 | -------------------------------------------------------------------------------- /misc/ci.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | 5 | ignores = [ 6 | '.git/', 7 | 'misc/afl-fuzz-tokens/', 8 | 'src/depfile_parser.cc', 9 | 'src/lexer.cc', 10 | ] 11 | 12 | error_count = 0 13 | 14 | def error(path: str, msg: str) -> None: 15 | global error_count 16 | error_count += 1 17 | print('\x1b[1;31m{}\x1b[0;31m{}\x1b[0m'.format(path, msg)) 18 | 19 | try: 20 | import git 21 | repo = git.Repo('.') 22 | except: 23 | repo = None 24 | 25 | for root, directory, filenames in os.walk('.'): 26 | for filename in filenames: 27 | path = os.path.join(root, filename)[2:] 28 | if any([path.startswith(x) for x in ignores]) or (repo is not None and repo.ignored(path)): 29 | continue 30 | with open(path, 'rb') as file: 31 | line_nr = 1 32 | try: 33 | for line in [x.decode() for x in file.readlines()]: 34 | if len(line) == 0 or line[-1] != '\n': 35 | error(path, ' missing newline at end of file.') 36 | if len(line) > 1: 37 | if line[-2] == '\r': 38 | error(path, ' has Windows line endings.') 39 | break 40 | if line[-2] == ' ' or line[-2] == '\t': 41 | error(path, ':{} has trailing whitespace.'.format(line_nr)) 42 | line_nr += 1 43 | except UnicodeError: 44 | pass # binary file 45 | 46 | exit(error_count) 47 | -------------------------------------------------------------------------------- /misc/inherited-fds.ninja: -------------------------------------------------------------------------------- 1 | # This build file prints out a list of open file descriptors in 2 | # Ninja subprocesses, to help verify we don't accidentally leak 3 | # any. 4 | 5 | # Because one fd leak was in the code managing multiple subprocesses, 6 | # this test brings up multiple subprocesses and then dumps the fd 7 | # table of the last one. 8 | 9 | # Use like: ./ninja -f misc/inherited-fds.ninja 10 | 11 | rule sleep 12 | command = sleep 10000 13 | 14 | rule dump 15 | command = sleep 1; ls -l /proc/self/fd; exit 1 16 | 17 | build all: phony a b c d e 18 | 19 | build a: sleep 20 | build b: sleep 21 | build c: sleep 22 | build d: sleep 23 | build e: dump 24 | -------------------------------------------------------------------------------- /misc/jobserver_test_helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright 2024 Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Simple utility used by the jobserver test. Wait for specific time, then write start/stop times to output file.""" 17 | 18 | import argparse 19 | import time 20 | import sys 21 | from pathlib import Path 22 | 23 | 24 | def main(): 25 | parser = argparse.ArgumentParser(description=__doc__) 26 | parser.add_argument( 27 | "--duration-ms", 28 | default="50", 29 | help="sleep duration in milliseconds (default 50)", 30 | ) 31 | parser.add_argument("output_file", type=Path, help="output file name.") 32 | args = parser.parse_args() 33 | 34 | now_time_ns = time.time_ns() 35 | time.sleep(int(args.duration_ms) / 1000.0) 36 | args.output_file.write_text(f"{now_time_ns}\n{time.time_ns()}\n") 37 | 38 | return 0 39 | 40 | 41 | if __name__ == "__main__": 42 | sys.exit(main()) 43 | -------------------------------------------------------------------------------- /misc/long-slow-build.ninja: -------------------------------------------------------------------------------- 1 | # An input file for running a "slow" build. 2 | # Use like: ninja -f misc/long-slow-build.ninja all 3 | 4 | rule sleep 5 | command = sleep 1 6 | description = SLEEP $out 7 | 8 | build 0: sleep README 9 | build 1: sleep README 10 | build 2: sleep README 11 | build 3: sleep README 12 | build 4: sleep README 13 | build 5: sleep README 14 | build 6: sleep README 15 | build 7: sleep README 16 | build 8: sleep README 17 | build 9: sleep README 18 | build 10: sleep 0 19 | build 11: sleep 1 20 | build 12: sleep 2 21 | build 13: sleep 3 22 | build 14: sleep 4 23 | build 15: sleep 5 24 | build 16: sleep 6 25 | build 17: sleep 7 26 | build 18: sleep 8 27 | build 19: sleep 9 28 | build 20: sleep 10 29 | build 21: sleep 11 30 | build 22: sleep 12 31 | build 23: sleep 13 32 | build 24: sleep 14 33 | build 25: sleep 15 34 | build 26: sleep 16 35 | build 27: sleep 17 36 | build 28: sleep 18 37 | build 29: sleep 19 38 | build all: phony 20 21 22 23 24 25 26 27 28 29 39 | -------------------------------------------------------------------------------- /misc/manifest_fuzzer.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdint.h" 16 | #include 17 | #include "disk_interface.h" 18 | #include "state.h" 19 | #include "manifest_parser.h" 20 | #include 21 | 22 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) 23 | { 24 | char build_file[256]; 25 | sprintf(build_file, "/tmp/build.ninja"); 26 | FILE *fp = fopen(build_file, "wb"); 27 | if (!fp) 28 | return 0; 29 | fwrite(data, size, 1, fp); 30 | fclose(fp); 31 | 32 | std::string err; 33 | RealDiskInterface disk_interface; 34 | State state; 35 | ManifestParser parser(&state, &disk_interface); 36 | 37 | parser.Load("/tmp/build.ninja", &err); 38 | 39 | std::__fs::filesystem::remove_all("/tmp/build.ninja"); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /misc/measure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2011 Google Inc. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """measure the runtime of a command by repeatedly running it. 18 | """ 19 | 20 | import time 21 | import subprocess 22 | import sys 23 | from typing import Union, List 24 | 25 | devnull = open('/dev/null', 'w') 26 | 27 | def run(cmd: Union[str, List[str]], repeat: int = 10) -> None: 28 | print('sampling:', end=' ') 29 | sys.stdout.flush() 30 | 31 | samples = [] 32 | for _ in range(repeat): 33 | start = time.time() 34 | subprocess.call(cmd, stdout=devnull, stderr=devnull) 35 | end = time.time() 36 | dt = (end - start) * 1000 37 | print('%dms' % int(dt), end=' ') 38 | sys.stdout.flush() 39 | samples.append(dt) 40 | print() 41 | 42 | # We're interested in the 'pure' runtime of the code, which is 43 | # conceptually the smallest time we'd see if we ran it enough times 44 | # such that it got the perfect time slices / disk cache hits. 45 | best = min(samples) 46 | # Also print how varied the outputs were in an attempt to make it 47 | # more obvious if something has gone terribly wrong. 48 | err = sum(s - best for s in samples) / float(len(samples)) 49 | print('estimate: %dms (mean err %.1fms)' % (best, err)) 50 | 51 | if __name__ == '__main__': 52 | if len(sys.argv) < 2: 53 | print('usage: measure.py command args...') 54 | sys.exit(1) 55 | run(cmd=sys.argv[1:]) 56 | -------------------------------------------------------------------------------- /misc/ninja.vim: -------------------------------------------------------------------------------- 1 | " ninja build file syntax. 2 | " Language: ninja build file as described at 3 | " http://ninja-build.org/manual.html 4 | " Version: 1.5 5 | " Last Change: 2018/04/05 6 | " Maintainer: Nicolas Weber 7 | " Version 1.4 of this script is in the upstream vim repository and will be 8 | " included in the next vim release. If you change this, please send your change 9 | " upstream. 10 | 11 | " ninja lexer and parser are at 12 | " https://github.com/ninja-build/ninja/blob/master/src/lexer.in.cc 13 | " https://github.com/ninja-build/ninja/blob/master/src/manifest_parser.cc 14 | 15 | if exists("b:current_syntax") 16 | finish 17 | endif 18 | 19 | let s:cpo_save = &cpo 20 | set cpo&vim 21 | 22 | syn case match 23 | 24 | " Comments are only matched when the # is at the beginning of the line (with 25 | " optional whitespace), as long as the prior line didn't end with a $ 26 | " continuation. 27 | syn match ninjaComment /\(\$\n\)\@" 33 | syn match ninjaKeyword "^rule\>" 34 | syn match ninjaKeyword "^pool\>" 35 | syn match ninjaKeyword "^default\>" 36 | syn match ninjaKeyword "^include\>" 37 | syn match ninjaKeyword "^subninja\>" 38 | 39 | " Both 'build' and 'rule' begin a variable scope that ends 40 | " on the first line without indent. 'rule' allows only a 41 | " limited set of magic variables, 'build' allows general 42 | " let assignments. 43 | " manifest_parser.cc, ParseRule() 44 | syn region ninjaRule start="^rule" end="^\ze\S" contains=TOP transparent 45 | syn keyword ninjaRuleCommand contained containedin=ninjaRule command 46 | \ deps depfile description generator 47 | \ pool restat rspfile rspfile_content 48 | 49 | syn region ninjaPool start="^pool" end="^\ze\S" contains=TOP transparent 50 | syn keyword ninjaPoolCommand contained containedin=ninjaPool depth 51 | 52 | " Strings are parsed as follows: 53 | " lexer.in.cc, ReadEvalString() 54 | " simple_varname = [a-zA-Z0-9_-]+; 55 | " varname = [a-zA-Z0-9_.-]+; 56 | " $$ -> $ 57 | " $\n -> line continuation 58 | " '$ ' -> escaped space 59 | " $simple_varname -> variable 60 | " ${varname} -> variable 61 | 62 | syn match ninjaDollar "\$\$" 63 | syn match ninjaWrapLineOperator "\$$" 64 | syn match ninjaSimpleVar "\$[a-zA-Z0-9_-]\+" 65 | syn match ninjaVar "\${[a-zA-Z0-9_.-]\+}" 66 | 67 | " operators are: 68 | " variable assignment = 69 | " rule definition : 70 | " implicit dependency | 71 | " order-only dependency || 72 | syn match ninjaOperator "\(=\|:\||\|||\)\ze\s" 73 | 74 | hi def link ninjaComment Comment 75 | hi def link ninjaKeyword Keyword 76 | hi def link ninjaRuleCommand Statement 77 | hi def link ninjaPoolCommand Statement 78 | hi def link ninjaDollar ninjaOperator 79 | hi def link ninjaWrapLineOperator ninjaOperator 80 | hi def link ninjaOperator Operator 81 | hi def link ninjaSimpleVar ninjaVar 82 | hi def link ninjaVar Identifier 83 | 84 | let b:current_syntax = "ninja" 85 | 86 | let &cpo = s:cpo_save 87 | unlet s:cpo_save 88 | -------------------------------------------------------------------------------- /misc/oss-fuzz/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | # Copyright 2020 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | ################################################################################ 17 | 18 | cmake -Bbuild-cmake -H. 19 | cmake --build build-cmake 20 | 21 | cd $SRC/ninja/misc 22 | 23 | $CXX $CXXFLAGS -fdiagnostics-color -I/src/ninja/src -o fuzzer.o -c manifest_fuzzer.cc 24 | 25 | find .. -name "*.o" -exec ar rcs fuzz_lib.a {} \; 26 | 27 | $CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzzer.o -o $OUT/fuzzer fuzz_lib.a 28 | 29 | zip $OUT/fuzzer_seed_corpus.zip $SRC/sample_ninja_build 30 | -------------------------------------------------------------------------------- /misc/oss-fuzz/sample_ninja_build: -------------------------------------------------------------------------------- 1 | # build.ninja 2 | cc = clang 3 | cflags = -Weverything 4 | 5 | rule compile 6 | command = $cc $cflags -c $in -o $out 7 | 8 | rule link 9 | command = $cc $in -o $out 10 | 11 | build hello.o: compile hello.c 12 | build hello: link hello.o 13 | 14 | default hello 15 | -------------------------------------------------------------------------------- /misc/packaging/ninja.spec: -------------------------------------------------------------------------------- 1 | Summary: Ninja is a small build system with a focus on speed. 2 | Name: ninja 3 | Version: %{ver} 4 | Release: %{rel}%{?dist} 5 | Group: Development/Tools 6 | License: Apache 2.0 7 | URL: https://github.com/ninja-build/ninja 8 | Source0: %{name}-%{version}-%{rel}.tar.gz 9 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{rel} 10 | 11 | BuildRequires: asciidoc 12 | 13 | %description 14 | Ninja is yet another build system. It takes as input the interdependencies of files (typically source code and output executables) and 15 | orchestrates building them, quickly. 16 | 17 | Ninja joins a sea of other build systems. Its distinguishing goal is to be fast. It is born from my work on the Chromium browser project, 18 | which has over 30,000 source files and whose other build systems (including one built from custom non-recursive Makefiles) can take ten 19 | seconds to start building after changing one file. Ninja is under a second. 20 | 21 | %prep 22 | %setup -q -n %{name}-%{version}-%{rel} 23 | 24 | %build 25 | echo Building.. 26 | ./configure.py --bootstrap 27 | ./ninja manual 28 | 29 | %install 30 | mkdir -p %{buildroot}%{_bindir} %{buildroot}%{_docdir} 31 | cp -p ninja %{buildroot}%{_bindir}/ 32 | 33 | %files 34 | %defattr(-, root, root) 35 | %doc COPYING README.md doc/manual.html 36 | %{_bindir}/* 37 | 38 | %clean 39 | rm -rf %{buildroot} 40 | 41 | #The changelog is built automatically from Git history 42 | %changelog 43 | -------------------------------------------------------------------------------- /misc/packaging/rpmbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo Building ninja RPMs.. 4 | GITROOT=$(git rev-parse --show-toplevel) 5 | cd $GITROOT 6 | 7 | VER=1.0 8 | REL=$(git rev-parse --short HEAD)git 9 | RPMTOPDIR=$GITROOT/rpm-build 10 | echo "Ver: $VER, Release: $REL" 11 | 12 | # Create tarball 13 | mkdir -p $RPMTOPDIR/{SOURCES,SPECS} 14 | git archive --format=tar --prefix=ninja-${VER}-${REL}/ HEAD | gzip -c > $RPMTOPDIR/SOURCES/ninja-${VER}-${REL}.tar.gz 15 | 16 | # Convert git log to RPM's ChangeLog format (shown with rpm -qp --changelog ) 17 | sed -e "s/%{ver}/$VER/" -e "s/%{rel}/$REL/" misc/packaging/ninja.spec > $RPMTOPDIR/SPECS/ninja.spec 18 | git log --format="* %cd %aN%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $RPMTOPDIR/SPECS/ninja.spec 19 | 20 | # Build SRC and binary RPMs 21 | rpmbuild --quiet \ 22 | --define "_topdir $RPMTOPDIR" \ 23 | --define "_rpmdir $PWD" \ 24 | --define "_srcrpmdir $PWD" \ 25 | --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ 26 | -ba $RPMTOPDIR/SPECS/ninja.spec && 27 | 28 | rm -rf $RPMTOPDIR && 29 | echo Done 30 | -------------------------------------------------------------------------------- /misc/zsh-completion: -------------------------------------------------------------------------------- 1 | #compdef ninja 2 | # Copyright 2011 Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Add the following to your .zshrc to tab-complete ninja targets 17 | # fpath=(path/to/ninja/misc/zsh-completion $fpath) 18 | 19 | (( $+functions[_ninja-get-targets] )) || _ninja-get-targets() { 20 | dir="." 21 | if [ -n "${opt_args[-C]}" ]; 22 | then 23 | eval dir="${opt_args[-C]}" 24 | fi 25 | file="build.ninja" 26 | if [ -n "${opt_args[-f]}" ]; 27 | then 28 | eval file="${opt_args[-f]}" 29 | fi 30 | targets_command="ninja -f \"${file}\" -C \"${dir}\" -t targets all" 31 | eval ${targets_command} 2>/dev/null | cut -d: -f1 32 | } 33 | 34 | (( $+functions[_ninja-get-tools] )) || _ninja-get-tools() { 35 | # remove the first line; remove the leading spaces; replace spaces with colon 36 | ninja -t list 2> /dev/null | sed -e '1d;s/^ *//;s/ \+/:/' 37 | } 38 | 39 | (( $+functions[_ninja-get-modes] )) || _ninja-get-modes() { 40 | # remove the first line; remove the last line; remove the leading spaces; replace spaces with colon 41 | ninja -d list 2> /dev/null | sed -e '1d;$d;s/^ *//;s/ \+/:/' 42 | } 43 | 44 | (( $+functions[_ninja-modes] )) || _ninja-modes() { 45 | local -a modes 46 | modes=(${(fo)"$(_ninja-get-modes)"}) 47 | _describe 'modes' modes 48 | } 49 | 50 | (( $+functions[_ninja-tools] )) || _ninja-tools() { 51 | local -a tools 52 | tools=(${(fo)"$(_ninja-get-tools)"}) 53 | _describe 'tools' tools 54 | } 55 | 56 | (( $+functions[_ninja-targets] )) || _ninja-targets() { 57 | local -a targets 58 | targets=(${(fo)"$(_ninja-get-targets)"}) 59 | _describe 'targets' targets 60 | } 61 | 62 | _arguments \ 63 | '(- *)'{-h,--help}'[Show help]' \ 64 | '(- *)--version[Print ninja version]' \ 65 | '-C+[Change to directory before doing anything else]:directories:_directories' \ 66 | '-f+[Specify input build file (default=build.ninja)]:files:_files' \ 67 | '-j+[Run N jobs in parallel (default=number of CPUs available)]:number of jobs' \ 68 | '-l+[Do not start new jobs if the load average is greater than N]:number of jobs' \ 69 | '-k+[Keep going until N jobs fail (default=1)]:number of jobs' \ 70 | '-n[Dry run (do not run commands but act like they succeeded)]' \ 71 | '(-v --verbose --quiet)'{-v,--verbose}'[Show all command lines while building]' \ 72 | "(-v --verbose --quiet)--quiet[Don't show progress status, just command output]" \ 73 | '-d+[Enable debugging (use -d list to list modes)]:modes:_ninja-modes' \ 74 | '-t+[Run a subtool (use -t list to list subtools)]:tools:_ninja-tools' \ 75 | '*::targets:_ninja-targets' 76 | -------------------------------------------------------------------------------- /src/browse.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "browse.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "build/browse_py.h" 24 | 25 | using namespace std; 26 | 27 | void RunBrowsePython(State* state, const char* ninja_command, 28 | const char* input_file, int argc, char* argv[]) { 29 | // Fork off a Python process and have it run our code via its stdin. 30 | // (Actually the Python process becomes the parent.) 31 | int pipefd[2]; 32 | if (pipe(pipefd) < 0) { 33 | perror("ninja: pipe"); 34 | return; 35 | } 36 | 37 | pid_t pid = fork(); 38 | if (pid < 0) { 39 | perror("ninja: fork"); 40 | return; 41 | } 42 | 43 | if (pid > 0) { // Parent. 44 | close(pipefd[1]); 45 | do { 46 | if (dup2(pipefd[0], 0) < 0) { 47 | perror("ninja: dup2"); 48 | break; 49 | } 50 | 51 | std::vector command; 52 | command.push_back(NINJA_PYTHON); 53 | command.push_back("-"); 54 | command.push_back("--ninja-command"); 55 | command.push_back(ninja_command); 56 | command.push_back("-f"); 57 | command.push_back(input_file); 58 | for (int i = 0; i < argc; i++) { 59 | command.push_back(argv[i]); 60 | } 61 | command.push_back(NULL); 62 | execvp(command[0], const_cast(&command[0])); 63 | if (errno == ENOENT) { 64 | printf("ninja: %s is required for the browse tool\n", NINJA_PYTHON); 65 | } else { 66 | perror("ninja: execvp"); 67 | } 68 | } while (false); 69 | _exit(1); 70 | } else { // Child. 71 | close(pipefd[0]); 72 | 73 | // Write the script file into the stdin of the Python process. 74 | // Only write n - 1 bytes, because Python 3.11 does not allow null 75 | // bytes in source code anymore, so avoid writing the null string 76 | // terminator. 77 | // See https://github.com/python/cpython/issues/96670 78 | auto kBrowsePyLength = sizeof(kBrowsePy) - 1; 79 | ssize_t len = write(pipefd[1], kBrowsePy, kBrowsePyLength); 80 | if (len < (ssize_t)kBrowsePyLength) 81 | perror("ninja: write"); 82 | close(pipefd[1]); 83 | exit(0); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/browse.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_BROWSE_H_ 16 | #define NINJA_BROWSE_H_ 17 | 18 | struct State; 19 | 20 | /// Run in "browse" mode, which execs a Python webserver. 21 | /// \a ninja_command is the command used to invoke ninja. 22 | /// \a args are the number of arguments to be passed to the Python script. 23 | /// \a argv are arguments to be passed to the Python script. 24 | /// This function does not return if it runs successfully. 25 | void RunBrowsePython(State* state, const char* ninja_command, 26 | const char* input_file, int argc, char* argv[]); 27 | 28 | #endif // NINJA_BROWSE_H_ 29 | -------------------------------------------------------------------------------- /src/build_log.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_BUILD_LOG_H_ 16 | #define NINJA_BUILD_LOG_H_ 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "hash_map.h" 24 | #include "load_status.h" 25 | #include "timestamp.h" 26 | #include "util.h" // uint64_t 27 | 28 | struct DiskInterface; 29 | struct Edge; 30 | 31 | /// Can answer questions about the manifest for the BuildLog. 32 | struct BuildLogUser { 33 | /// Return if a given output is no longer part of the build manifest. 34 | /// This is only called during recompaction and doesn't have to be fast. 35 | virtual bool IsPathDead(StringPiece s) const = 0; 36 | }; 37 | 38 | /// Store a log of every command ran for every build. 39 | /// It has a few uses: 40 | /// 41 | /// 1) (hashes of) command lines for existing output files, so we know 42 | /// when we need to rebuild due to the command changing 43 | /// 2) timing information, perhaps for generating reports 44 | /// 3) restat information 45 | struct BuildLog { 46 | BuildLog(); 47 | ~BuildLog(); 48 | 49 | /// Prepares writing to the log file without actually opening it - that will 50 | /// happen when/if it's needed 51 | bool OpenForWrite(const std::string& path, const BuildLogUser& user, 52 | std::string* err); 53 | bool RecordCommand(Edge* edge, int start_time, int end_time, 54 | TimeStamp mtime = 0); 55 | void Close(); 56 | 57 | /// Load the on-disk log. 58 | LoadStatus Load(const std::string& path, std::string* err); 59 | 60 | struct LogEntry { 61 | std::string output; 62 | uint64_t command_hash = 0; 63 | int start_time = 0; 64 | int end_time = 0; 65 | TimeStamp mtime = 0; 66 | 67 | static uint64_t HashCommand(StringPiece command); 68 | 69 | // Used by tests. 70 | bool operator==(const LogEntry& o) const { 71 | return output == o.output && command_hash == o.command_hash && 72 | start_time == o.start_time && end_time == o.end_time && 73 | mtime == o.mtime; 74 | } 75 | 76 | explicit LogEntry(std::string output); 77 | LogEntry(const std::string& output, uint64_t command_hash, 78 | int start_time, int end_time, TimeStamp mtime); 79 | }; 80 | 81 | /// Lookup a previously-run command by its output path. 82 | LogEntry* LookupByOutput(const std::string& path); 83 | 84 | /// Serialize an entry into a log file. 85 | bool WriteEntry(FILE* f, const LogEntry& entry); 86 | 87 | /// Rewrite the known log entries, throwing away old data. 88 | bool Recompact(const std::string& path, const BuildLogUser& user, 89 | std::string* err); 90 | 91 | /// Restat all outputs in the log 92 | bool Restat(StringPiece path, const DiskInterface& disk_interface, 93 | int output_count, char** outputs, std::string* err); 94 | 95 | typedef ExternalStringHashMap>::Type Entries; 96 | const Entries& entries() const { return entries_; } 97 | 98 | private: 99 | /// Should be called before using log_file_. When false is returned, errno 100 | /// will be set. 101 | bool OpenForWriteIfNeeded(); 102 | 103 | Entries entries_; 104 | FILE* log_file_ = nullptr; 105 | std::string log_file_path_; 106 | bool needs_recompaction_ = false; 107 | }; 108 | 109 | #endif // NINJA_BUILD_LOG_H_ 110 | -------------------------------------------------------------------------------- /src/build_log_perftest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "build_log.h" 19 | #include "graph.h" 20 | #include "manifest_parser.h" 21 | #include "state.h" 22 | #include "util.h" 23 | #include "metrics.h" 24 | 25 | #ifndef _WIN32 26 | #include 27 | #endif 28 | 29 | using namespace std; 30 | 31 | const char kTestFilename[] = "BuildLogPerfTest-tempfile"; 32 | 33 | struct NoDeadPaths : public BuildLogUser { 34 | virtual bool IsPathDead(StringPiece) const { return false; } 35 | }; 36 | 37 | bool WriteTestData(string* err) { 38 | BuildLog log; 39 | 40 | NoDeadPaths no_dead_paths; 41 | if (!log.OpenForWrite(kTestFilename, no_dead_paths, err)) 42 | return false; 43 | 44 | /* 45 | A histogram of command lengths in chromium. For example, 407 builds, 46 | 1.4% of all builds, had commands longer than 32 bytes but shorter than 64. 47 | 32 407 1.4% 48 | 64 183 0.6% 49 | 128 1461 5.1% 50 | 256 791 2.8% 51 | 512 1314 4.6% 52 | 1024 6114 21.3% 53 | 2048 11759 41.0% 54 | 4096 2056 7.2% 55 | 8192 4567 15.9% 56 | 16384 13 0.0% 57 | 32768 4 0.0% 58 | 65536 5 0.0% 59 | The average command length is 4.1 kB and there were 28674 commands in total, 60 | which makes for a total log size of ~120 MB (also counting output filenames). 61 | 62 | Based on this, write 30000 many 4 kB long command lines. 63 | */ 64 | 65 | // ManifestParser is the only object allowed to create Rules. 66 | const size_t kRuleSize = 4000; 67 | string long_rule_command = "gcc "; 68 | for (int i = 0; long_rule_command.size() < kRuleSize; ++i) { 69 | char buf[80]; 70 | sprintf(buf, "-I../../and/arbitrary/but/fairly/long/path/suffixed/%d ", i); 71 | long_rule_command += buf; 72 | } 73 | long_rule_command += "$in -o $out\n"; 74 | 75 | State state; 76 | ManifestParser parser(&state, NULL); 77 | if (!parser.ParseTest("rule cxx\n command = " + long_rule_command, err)) 78 | return false; 79 | 80 | // Create build edges. Using ManifestParser is as fast as using the State api 81 | // for edge creation, so just use that. 82 | const int kNumCommands = 30000; 83 | string build_rules; 84 | for (int i = 0; i < kNumCommands; ++i) { 85 | char buf[80]; 86 | sprintf(buf, "build input%d.o: cxx input%d.cc\n", i, i); 87 | build_rules += buf; 88 | } 89 | 90 | if (!parser.ParseTest(build_rules, err)) 91 | return false; 92 | 93 | for (int i = 0; i < kNumCommands; ++i) { 94 | log.RecordCommand(state.edges_[i], 95 | /*start_time=*/100 * i, 96 | /*end_time=*/100 * i + 1, 97 | /*mtime=*/0); 98 | } 99 | 100 | return true; 101 | } 102 | 103 | int main() { 104 | vector times; 105 | string err; 106 | 107 | if (!WriteTestData(&err)) { 108 | fprintf(stderr, "Failed to write test data: %s\n", err.c_str()); 109 | return 1; 110 | } 111 | 112 | { 113 | // Read once to warm up disk cache. 114 | BuildLog log; 115 | if (log.Load(kTestFilename, &err) == LOAD_ERROR) { 116 | fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); 117 | return 1; 118 | } 119 | } 120 | const int kNumRepetitions = 5; 121 | for (int i = 0; i < kNumRepetitions; ++i) { 122 | int64_t start = GetTimeMillis(); 123 | BuildLog log; 124 | if (log.Load(kTestFilename, &err) == LOAD_ERROR) { 125 | fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); 126 | return 1; 127 | } 128 | int delta = (int)(GetTimeMillis() - start); 129 | printf("%dms\n", delta); 130 | times.push_back(delta); 131 | } 132 | 133 | int min = times[0]; 134 | int max = times[0]; 135 | float total = 0; 136 | for (size_t i = 0; i < times.size(); ++i) { 137 | total += times[i]; 138 | if (times[i] < min) 139 | min = times[i]; 140 | else if (times[i] > max) 141 | max = times[i]; 142 | } 143 | 144 | printf("min %dms max %dms avg %.1fms\n", 145 | min, max, total / times.size()); 146 | 147 | platformAwareUnlink(kTestFilename); 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /src/canon_perftest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "util.h" 19 | #include "metrics.h" 20 | 21 | using namespace std; 22 | 23 | const char kPath[] = 24 | "../../third_party/WebKit/Source/WebCore/" 25 | "platform/leveldb/LevelDBWriteBatch.cpp"; 26 | 27 | int main() { 28 | vector times; 29 | 30 | char buf[200]; 31 | size_t len = strlen(kPath); 32 | strcpy(buf, kPath); 33 | 34 | for (int j = 0; j < 5; ++j) { 35 | const int kNumRepetitions = 2000000; 36 | int64_t start = GetTimeMillis(); 37 | uint64_t slash_bits; 38 | for (int i = 0; i < kNumRepetitions; ++i) { 39 | CanonicalizePath(buf, &len, &slash_bits); 40 | } 41 | int delta = (int)(GetTimeMillis() - start); 42 | times.push_back(delta); 43 | } 44 | 45 | int min = times[0]; 46 | int max = times[0]; 47 | float total = 0; 48 | for (size_t i = 0; i < times.size(); ++i) { 49 | total += times[i]; 50 | if (times[i] < min) 51 | min = times[i]; 52 | else if (times[i] > max) 53 | max = times[i]; 54 | } 55 | 56 | printf("min %dms max %dms avg %.1fms\n", 57 | min, max, total / times.size()); 58 | } 59 | -------------------------------------------------------------------------------- /src/clean.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_CLEAN_H_ 16 | #define NINJA_CLEAN_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "build.h" 22 | #include "dyndep.h" 23 | #include "build_log.h" 24 | 25 | struct State; 26 | struct Node; 27 | struct Rule; 28 | struct DiskInterface; 29 | 30 | struct Cleaner { 31 | /// Build a cleaner object with the given @a disk_interface 32 | Cleaner(State* state, 33 | const BuildConfig& config, 34 | DiskInterface* disk_interface); 35 | 36 | /// Clean the given @a target and all the file built for it. 37 | /// @return non-zero if an error occurs. 38 | int CleanTarget(Node* target); 39 | /// Clean the given target @a target. 40 | /// @return non-zero if an error occurs. 41 | int CleanTarget(const char* target); 42 | /// Clean the given target @a targets. 43 | /// @return non-zero if an error occurs. 44 | int CleanTargets(int target_count, char* targets[]); 45 | 46 | /// Clean all built files, except for files created by generator rules. 47 | /// @param generator If set, also clean files created by generator rules. 48 | /// @return non-zero if an error occurs. 49 | int CleanAll(bool generator = false); 50 | 51 | /// Clean all the file built with the given rule @a rule. 52 | /// @return non-zero if an error occurs. 53 | int CleanRule(const Rule* rule); 54 | /// Clean the file produced by the given @a rule. 55 | /// @return non-zero if an error occurs. 56 | int CleanRule(const char* rule); 57 | /// Clean the file produced by the given @a rules. 58 | /// @return non-zero if an error occurs. 59 | int CleanRules(int rule_count, char* rules[]); 60 | /// Clean the files produced by previous builds that are no longer in the 61 | /// manifest. 62 | /// @return non-zero if an error occurs. 63 | int CleanDead(const BuildLog::Entries& entries); 64 | 65 | /// @return the number of file cleaned. 66 | int cleaned_files_count() const { 67 | return cleaned_files_count_; 68 | } 69 | 70 | /// @return whether the cleaner is in verbose mode. 71 | bool IsVerbose() const { 72 | return (config_.verbosity != BuildConfig::QUIET 73 | && (config_.verbosity == BuildConfig::VERBOSE || config_.dry_run)); 74 | } 75 | 76 | private: 77 | /// Remove the file @a path. 78 | /// @return whether the file has been removed. 79 | int RemoveFile(const std::string& path); 80 | /// @returns whether the file @a path exists. 81 | bool FileExists(const std::string& path); 82 | void Report(const std::string& path); 83 | 84 | /// Remove the given @a path file only if it has not been already removed. 85 | void Remove(const std::string& path); 86 | /// @return whether the given @a path has already been removed. 87 | bool IsAlreadyRemoved(const std::string& path); 88 | /// Remove the depfile and rspfile for an Edge. 89 | void RemoveEdgeFiles(Edge* edge); 90 | 91 | /// Helper recursive method for CleanTarget(). 92 | void DoCleanTarget(Node* target); 93 | void PrintHeader(); 94 | void PrintFooter(); 95 | void DoCleanRule(const Rule* rule); 96 | void Reset(); 97 | 98 | /// Load dependencies from dyndep bindings. 99 | void LoadDyndeps(); 100 | 101 | State* state_; 102 | const BuildConfig& config_; 103 | DyndepLoader dyndep_loader_; 104 | std::set removed_; 105 | std::set cleaned_; 106 | int cleaned_files_count_; 107 | DiskInterface* disk_interface_; 108 | int status_; 109 | }; 110 | 111 | #endif // NINJA_CLEAN_H_ 112 | -------------------------------------------------------------------------------- /src/clparser.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "clparser.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "metrics.h" 22 | #include "string_piece_util.h" 23 | 24 | #ifdef _WIN32 25 | #include "includes_normalize.h" 26 | #include "string_piece.h" 27 | #else 28 | #include "util.h" 29 | #endif 30 | 31 | using namespace std; 32 | 33 | namespace { 34 | 35 | /// Return true if \a input ends with \a needle. 36 | bool EndsWith(const string& input, const string& needle) { 37 | return (input.size() >= needle.size() && 38 | input.substr(input.size() - needle.size()) == needle); 39 | } 40 | 41 | } // anonymous namespace 42 | 43 | // static 44 | string CLParser::FilterShowIncludes(const string& line, 45 | const string& deps_prefix) { 46 | const string kDepsPrefixEnglish = "Note: including file: "; 47 | const char* in = line.c_str(); 48 | const char* end = in + line.size(); 49 | const string& prefix = deps_prefix.empty() ? kDepsPrefixEnglish : deps_prefix; 50 | if (end - in > (int)prefix.size() && 51 | memcmp(in, prefix.c_str(), (int)prefix.size()) == 0) { 52 | in += prefix.size(); 53 | while (*in == ' ') 54 | ++in; 55 | return line.substr(in - line.c_str()); 56 | } 57 | return ""; 58 | } 59 | 60 | // static 61 | bool CLParser::IsSystemInclude(string path) { 62 | transform(path.begin(), path.end(), path.begin(), ToLowerASCII); 63 | // TODO: this is a heuristic, perhaps there's a better way? 64 | return (path.find("program files") != string::npos || 65 | path.find("microsoft visual studio") != string::npos); 66 | } 67 | 68 | // static 69 | bool CLParser::FilterInputFilename(string line) { 70 | transform(line.begin(), line.end(), line.begin(), ToLowerASCII); 71 | // TODO: other extensions, like .asm? 72 | return EndsWith(line, ".c") || 73 | EndsWith(line, ".cc") || 74 | EndsWith(line, ".cxx") || 75 | EndsWith(line, ".cpp") || 76 | EndsWith(line, ".c++"); 77 | } 78 | 79 | // static 80 | bool CLParser::Parse(const string& output, const string& deps_prefix, 81 | string* filtered_output, string* err) { 82 | METRIC_RECORD("CLParser::Parse"); 83 | 84 | // Loop over all lines in the output to process them. 85 | assert(&output != filtered_output); 86 | size_t start = 0; 87 | bool seen_show_includes = false; 88 | #ifdef _WIN32 89 | IncludesNormalize normalizer("."); 90 | #endif 91 | 92 | while (start < output.size()) { 93 | size_t end = output.find_first_of("\r\n", start); 94 | if (end == string::npos) 95 | end = output.size(); 96 | string line = output.substr(start, end - start); 97 | 98 | string include = FilterShowIncludes(line, deps_prefix); 99 | if (!include.empty()) { 100 | seen_show_includes = true; 101 | string normalized; 102 | #ifdef _WIN32 103 | if (!normalizer.Normalize(include, &normalized, err)) 104 | return false; 105 | #else 106 | // TODO: should this make the path relative to cwd? 107 | normalized = include; 108 | uint64_t slash_bits; 109 | CanonicalizePath(&normalized, &slash_bits); 110 | #endif 111 | if (!IsSystemInclude(normalized)) 112 | includes_.insert(normalized); 113 | } else if (!seen_show_includes && FilterInputFilename(line)) { 114 | // Drop it. 115 | // TODO: if we support compiling multiple output files in a single 116 | // cl.exe invocation, we should stash the filename. 117 | } else { 118 | filtered_output->append(line); 119 | filtered_output->append("\n"); 120 | } 121 | 122 | if (end < output.size() && output[end] == '\r') 123 | ++end; 124 | if (end < output.size() && output[end] == '\n') 125 | ++end; 126 | start = end; 127 | } 128 | 129 | return true; 130 | } 131 | -------------------------------------------------------------------------------- /src/clparser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_CLPARSER_H_ 16 | #define NINJA_CLPARSER_H_ 17 | 18 | #include 19 | #include 20 | 21 | /// Visual Studio's cl.exe requires some massaging to work with Ninja; 22 | /// for example, it emits include information on stderr in a funny 23 | /// format when building with /showIncludes. This class parses this 24 | /// output. 25 | struct CLParser { 26 | /// Parse a line of cl.exe output and extract /showIncludes info. 27 | /// If a dependency is extracted, returns a nonempty string. 28 | /// Exposed for testing. 29 | static std::string FilterShowIncludes(const std::string& line, 30 | const std::string& deps_prefix); 31 | 32 | /// Return true if a mentioned include file is a system path. 33 | /// Filtering these out reduces dependency information considerably. 34 | static bool IsSystemInclude(std::string path); 35 | 36 | /// Parse a line of cl.exe output and return true if it looks like 37 | /// it's printing an input filename. This is a heuristic but it appears 38 | /// to be the best we can do. 39 | /// Exposed for testing. 40 | static bool FilterInputFilename(std::string line); 41 | 42 | /// Parse the full output of cl, filling filtered_output with the text that 43 | /// should be printed (if any). Returns true on success, or false with err 44 | /// filled. output must not be the same object as filtered_object. 45 | bool Parse(const std::string& output, const std::string& deps_prefix, 46 | std::string* filtered_output, std::string* err); 47 | 48 | std::set includes_; 49 | }; 50 | 51 | #endif // NINJA_CLPARSER_H_ 52 | -------------------------------------------------------------------------------- /src/command_collector.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_COMMAND_COLLECTOR_H_ 16 | #define NINJA_COMMAND_COLLECTOR_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "graph.h" 23 | 24 | /// Collects the transitive set of edges that lead into a given set 25 | /// of starting nodes. Used to implement the `compdb-targets` tool. 26 | /// 27 | /// When collecting inputs, the outputs of phony edges are always ignored 28 | /// from the result, but are followed by the dependency walk. 29 | /// 30 | /// Usage is: 31 | /// - Create instance. 32 | /// - Call CollectFrom() for each root node to collect edges from. 33 | /// - Call TakeResult() to retrieve the list of edges. 34 | /// 35 | struct CommandCollector { 36 | void CollectFrom(const Node* node) { 37 | assert(node); 38 | 39 | if (!visited_nodes_.insert(node).second) 40 | return; 41 | 42 | Edge* edge = node->in_edge(); 43 | if (!edge || !visited_edges_.insert(edge).second) 44 | return; 45 | 46 | for (Node* input_node : edge->inputs_) 47 | CollectFrom(input_node); 48 | 49 | if (!edge->is_phony()) 50 | in_edges.push_back(edge); 51 | } 52 | 53 | private: 54 | std::unordered_set visited_nodes_; 55 | std::unordered_set visited_edges_; 56 | 57 | /// we use a vector to preserve order from requisites to their dependents. 58 | /// This may help LSP server performance in languages that support modules, 59 | /// but it also ensures that the output of `-t compdb-targets foo` is 60 | /// consistent, which is useful in regression tests. 61 | public: 62 | std::vector in_edges; 63 | }; 64 | 65 | #endif // NINJA_COMMAND_COLLECTOR_H_ 66 | -------------------------------------------------------------------------------- /src/debug_flags.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "graph.h" 21 | 22 | bool g_explaining = false; 23 | 24 | bool g_keep_depfile = false; 25 | 26 | bool g_keep_rsp = false; 27 | 28 | bool g_experimental_statcache = true; 29 | -------------------------------------------------------------------------------- /src/debug_flags.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_EXPLAIN_H_ 16 | #define NINJA_EXPLAIN_H_ 17 | 18 | #include 19 | 20 | struct Edge; 21 | struct Node; 22 | 23 | extern bool g_explaining; 24 | 25 | extern bool g_keep_depfile; 26 | 27 | extern bool g_keep_rsp; 28 | 29 | extern bool g_experimental_statcache; 30 | 31 | #endif // NINJA_EXPLAIN_H_ 32 | -------------------------------------------------------------------------------- /src/depfile_parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_DEPFILE_PARSER_H_ 16 | #define NINJA_DEPFILE_PARSER_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "string_piece.h" 22 | 23 | struct DepfileParserOptions { 24 | DepfileParserOptions() {} 25 | }; 26 | 27 | /// Parser for the dependency information emitted by gcc's -M flags. 28 | struct DepfileParser { 29 | explicit DepfileParser(DepfileParserOptions options = 30 | DepfileParserOptions()); 31 | 32 | /// Parse an input file. Input must be NUL-terminated. 33 | /// Warning: may mutate the content in-place and parsed StringPieces are 34 | /// pointers within it. 35 | bool Parse(std::string* content, std::string* err); 36 | 37 | std::vector outs_; 38 | std::vector ins_; 39 | DepfileParserOptions options_; 40 | }; 41 | 42 | #endif // NINJA_DEPFILE_PARSER_H_ 43 | -------------------------------------------------------------------------------- /src/depfile_parser_perftest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "depfile_parser.h" 19 | #include "util.h" 20 | #include "metrics.h" 21 | 22 | using namespace std; 23 | 24 | int main(int argc, char* argv[]) { 25 | if (argc < 2) { 26 | printf("usage: %s \n", argv[0]); 27 | return 1; 28 | } 29 | 30 | vector times; 31 | for (int i = 1; i < argc; ++i) { 32 | const char* filename = argv[i]; 33 | 34 | for (int limit = 1 << 10; limit < (1<<20); limit *= 2) { 35 | int64_t start = GetTimeMillis(); 36 | for (int rep = 0; rep < limit; ++rep) { 37 | string buf; 38 | string err; 39 | if (ReadFile(filename, &buf, &err) < 0) { 40 | printf("%s: %s\n", filename, err.c_str()); 41 | return 1; 42 | } 43 | 44 | DepfileParser parser; 45 | if (!parser.Parse(&buf, &err)) { 46 | printf("%s: %s\n", filename, err.c_str()); 47 | return 1; 48 | } 49 | } 50 | int64_t end = GetTimeMillis(); 51 | 52 | if (end - start > 100) { 53 | int delta = (int)(end - start); 54 | float time = delta*1000 / (float)limit; 55 | printf("%s: %.1fus\n", filename, time); 56 | times.push_back(time); 57 | break; 58 | } 59 | } 60 | } 61 | 62 | if (!times.empty()) { 63 | float min = times[0]; 64 | float max = times[0]; 65 | float total = 0; 66 | for (size_t i = 0; i < times.size(); ++i) { 67 | total += times[i]; 68 | if (times[i] < min) 69 | min = times[i]; 70 | else if (times[i] > max) 71 | max = times[i]; 72 | } 73 | 74 | printf("min %.1fus max %.1fus avg %.1fus\n", 75 | min, max, total / times.size()); 76 | } 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /src/disk_interface.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_DISK_INTERFACE_H_ 16 | #define NINJA_DISK_INTERFACE_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "timestamp.h" 22 | 23 | /// Interface for reading files from disk. See DiskInterface for details. 24 | /// This base offers the minimum interface needed just to read files. 25 | struct FileReader { 26 | virtual ~FileReader() {} 27 | 28 | /// Result of ReadFile. 29 | enum Status { 30 | Okay, 31 | NotFound, 32 | OtherError 33 | }; 34 | 35 | /// Read and store in given string. On success, return Okay. 36 | /// On error, return another Status and fill |err|. 37 | virtual Status ReadFile(const std::string& path, std::string* contents, 38 | std::string* err) = 0; 39 | }; 40 | 41 | /// Interface for accessing the disk. 42 | /// 43 | /// Abstract so it can be mocked out for tests. The real implementation 44 | /// is RealDiskInterface. 45 | struct DiskInterface: public FileReader { 46 | /// stat() a file, returning the mtime, or 0 if missing and -1 on 47 | /// other errors. 48 | virtual TimeStamp Stat(const std::string& path, std::string* err) const = 0; 49 | 50 | /// Create a directory, returning false on failure. 51 | virtual bool MakeDir(const std::string& path) = 0; 52 | 53 | /// Create a file, with the specified name and contents 54 | /// Returns true on success, false on failure 55 | virtual bool WriteFile(const std::string& path, 56 | const std::string& contents) = 0; 57 | 58 | /// Remove the file named @a path. It behaves like 'rm -f path' so no errors 59 | /// are reported if it does not exists. 60 | /// @returns 0 if the file has been removed, 61 | /// 1 if the file does not exist, and 62 | /// -1 if an error occurs. 63 | virtual int RemoveFile(const std::string& path) = 0; 64 | 65 | /// Create all the parent directories for path; like mkdir -p 66 | /// `basename path`. 67 | bool MakeDirs(const std::string& path); 68 | }; 69 | 70 | /// Implementation of DiskInterface that actually hits the disk. 71 | struct RealDiskInterface : public DiskInterface { 72 | RealDiskInterface(); 73 | virtual ~RealDiskInterface() {} 74 | virtual TimeStamp Stat(const std::string& path, std::string* err) const; 75 | virtual bool MakeDir(const std::string& path); 76 | virtual bool WriteFile(const std::string& path, const std::string& contents); 77 | virtual Status ReadFile(const std::string& path, std::string* contents, 78 | std::string* err); 79 | virtual int RemoveFile(const std::string& path); 80 | 81 | /// Whether stat information can be cached. Only has an effect on Windows. 82 | void AllowStatCache(bool allow); 83 | 84 | #ifdef _WIN32 85 | /// Whether long paths are enabled. Only has an effect on Windows. 86 | bool AreLongPathsEnabled() const; 87 | #endif 88 | 89 | private: 90 | #ifdef _WIN32 91 | /// Whether stat information can be cached. 92 | bool use_cache_; 93 | 94 | /// Whether long paths are enabled. 95 | bool long_paths_enabled_; 96 | 97 | typedef std::map DirCache; 98 | // TODO: Neither a map nor a hashmap seems ideal here. If the statcache 99 | // works out, come up with a better data structure. 100 | typedef std::map Cache; 101 | mutable Cache cache_; 102 | #endif 103 | }; 104 | 105 | #endif // NINJA_DISK_INTERFACE_H_ 106 | -------------------------------------------------------------------------------- /src/dyndep.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "dyndep.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "debug_flags.h" 21 | #include "disk_interface.h" 22 | #include "dyndep_parser.h" 23 | #include "explanations.h" 24 | #include "graph.h" 25 | #include "state.h" 26 | #include "util.h" 27 | 28 | using namespace std; 29 | 30 | bool DyndepLoader::LoadDyndeps(Node* node, std::string* err) const { 31 | DyndepFile ddf; 32 | return LoadDyndeps(node, &ddf, err); 33 | } 34 | 35 | bool DyndepLoader::LoadDyndeps(Node* node, DyndepFile* ddf, 36 | std::string* err) const { 37 | // We are loading the dyndep file now so it is no longer pending. 38 | node->set_dyndep_pending(false); 39 | 40 | // Load the dyndep information from the file. 41 | explanations_.Record(node, "loading dyndep file '%s'", node->path().c_str()); 42 | 43 | if (!LoadDyndepFile(node, ddf, err)) 44 | return false; 45 | 46 | // Update each edge that specified this node as its dyndep binding. 47 | std::vector const& out_edges = node->out_edges(); 48 | for (Edge* edge : out_edges) { 49 | if (edge->dyndep_ != node) 50 | continue; 51 | 52 | DyndepFile::iterator ddi = ddf->find(edge); 53 | if (ddi == ddf->end()) { 54 | *err = ("'" + edge->outputs_[0]->path() + "' " 55 | "not mentioned in its dyndep file " 56 | "'" + node->path() + "'"); 57 | return false; 58 | } 59 | 60 | ddi->second.used_ = true; 61 | Dyndeps const& dyndeps = ddi->second; 62 | if (!UpdateEdge(edge, &dyndeps, err)) { 63 | return false; 64 | } 65 | } 66 | 67 | // Reject extra outputs in dyndep file. 68 | for (const auto& dyndep_output : *ddf) { 69 | if (!dyndep_output.second.used_) { 70 | Edge* const edge = dyndep_output.first; 71 | *err = ("dyndep file '" + node->path() + "' mentions output " 72 | "'" + edge->outputs_[0]->path() + "' whose build statement " 73 | "does not have a dyndep binding for the file"); 74 | return false; 75 | } 76 | } 77 | 78 | return true; 79 | } 80 | 81 | bool DyndepLoader::UpdateEdge(Edge* edge, Dyndeps const* dyndeps, 82 | std::string* err) const { 83 | // Add dyndep-discovered bindings to the edge. 84 | // We know the edge already has its own binding 85 | // scope because it has a "dyndep" binding. 86 | if (dyndeps->restat_) 87 | edge->env_->AddBinding("restat", "1"); 88 | 89 | // Add the dyndep-discovered outputs to the edge. 90 | edge->outputs_.insert(edge->outputs_.end(), 91 | dyndeps->implicit_outputs_.begin(), 92 | dyndeps->implicit_outputs_.end()); 93 | edge->implicit_outs_ += dyndeps->implicit_outputs_.size(); 94 | 95 | // Add this edge as incoming to each new output. 96 | for (Node* node : dyndeps->implicit_outputs_) { 97 | if (node->in_edge()) { 98 | // This node already has an edge producing it. 99 | *err = "multiple rules generate " + node->path(); 100 | return false; 101 | } 102 | node->set_in_edge(edge); 103 | } 104 | 105 | // Add the dyndep-discovered inputs to the edge. 106 | edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_, 107 | dyndeps->implicit_inputs_.begin(), 108 | dyndeps->implicit_inputs_.end()); 109 | edge->implicit_deps_ += dyndeps->implicit_inputs_.size(); 110 | 111 | // Add this edge as outgoing from each new input. 112 | for (Node* node : dyndeps->implicit_inputs_) 113 | node->AddOutEdge(edge); 114 | 115 | return true; 116 | } 117 | 118 | bool DyndepLoader::LoadDyndepFile(Node* file, DyndepFile* ddf, 119 | std::string* err) const { 120 | DyndepParser parser(state_, disk_interface_, ddf); 121 | return parser.Load(file->path(), err); 122 | } 123 | -------------------------------------------------------------------------------- /src/dyndep.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_DYNDEP_LOADER_H_ 16 | #define NINJA_DYNDEP_LOADER_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "explanations.h" 23 | 24 | struct DiskInterface; 25 | struct Edge; 26 | struct Node; 27 | struct State; 28 | 29 | /// Store dynamically-discovered dependency information for one edge. 30 | struct Dyndeps { 31 | Dyndeps() : used_(false), restat_(false) {} 32 | bool used_; 33 | bool restat_; 34 | std::vector implicit_inputs_; 35 | std::vector implicit_outputs_; 36 | }; 37 | 38 | /// Store data loaded from one dyndep file. Map from an edge 39 | /// to its dynamically-discovered dependency information. 40 | /// This is a struct rather than a typedef so that we can 41 | /// forward-declare it in other headers. 42 | struct DyndepFile: public std::map {}; 43 | 44 | /// DyndepLoader loads dynamically discovered dependencies, as 45 | /// referenced via the "dyndep" attribute in build files. 46 | struct DyndepLoader { 47 | DyndepLoader(State* state, DiskInterface* disk_interface, 48 | Explanations* explanations = nullptr) 49 | : state_(state), disk_interface_(disk_interface), 50 | explanations_(explanations) {} 51 | 52 | /// Load a dyndep file from the given node's path and update the 53 | /// build graph with the new information. One overload accepts 54 | /// a caller-owned 'DyndepFile' object in which to store the 55 | /// information loaded from the dyndep file. 56 | bool LoadDyndeps(Node* node, std::string* err) const; 57 | bool LoadDyndeps(Node* node, DyndepFile* ddf, std::string* err) const; 58 | 59 | private: 60 | bool LoadDyndepFile(Node* file, DyndepFile* ddf, std::string* err) const; 61 | 62 | bool UpdateEdge(Edge* edge, Dyndeps const* dyndeps, std::string* err) const; 63 | 64 | State* state_; 65 | DiskInterface* disk_interface_; 66 | mutable OptionalExplanations explanations_; 67 | }; 68 | 69 | #endif // NINJA_DYNDEP_LOADER_H_ 70 | -------------------------------------------------------------------------------- /src/dyndep_parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_DYNDEP_PARSER_H_ 16 | #define NINJA_DYNDEP_PARSER_H_ 17 | 18 | #include "eval_env.h" 19 | #include "parser.h" 20 | 21 | struct DyndepFile; 22 | struct EvalString; 23 | 24 | /// Parses dyndep files. 25 | struct DyndepParser: public Parser { 26 | DyndepParser(State* state, FileReader* file_reader, 27 | DyndepFile* dyndep_file); 28 | 29 | /// Parse a text string of input. Used by tests. 30 | bool ParseTest(const std::string& input, std::string* err) { 31 | return Parse("input", input, err); 32 | } 33 | 34 | private: 35 | /// Parse a file, given its contents as a string. 36 | bool Parse(const std::string& filename, const std::string& input, 37 | std:: string* err); 38 | 39 | bool ParseDyndepVersion(std::string* err); 40 | bool ParseLet(std::string* key, EvalString* val, std::string* err); 41 | bool ParseEdge(std::string* err); 42 | 43 | DyndepFile* dyndep_file_; 44 | BindingEnv env_; 45 | }; 46 | 47 | #endif // NINJA_DYNDEP_PARSER_H_ 48 | -------------------------------------------------------------------------------- /src/edit_distance.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "edit_distance.h" 16 | 17 | #include 18 | #include 19 | 20 | using namespace std; 21 | 22 | int EditDistance(const StringPiece& s1, 23 | const StringPiece& s2, 24 | bool allow_replacements, 25 | int max_edit_distance) { 26 | // The algorithm implemented below is the "classic" 27 | // dynamic-programming algorithm for computing the Levenshtein 28 | // distance, which is described here: 29 | // 30 | // http://en.wikipedia.org/wiki/Levenshtein_distance 31 | // 32 | // Although the algorithm is typically described using an m x n 33 | // array, only one row plus one element are used at a time, so this 34 | // implementation just keeps one vector for the row. To update one entry, 35 | // only the entries to the left, top, and top-left are needed. The left 36 | // entry is in row[x-1], the top entry is what's in row[x] from the last 37 | // iteration, and the top-left entry is stored in previous. 38 | int m = s1.len_; 39 | int n = s2.len_; 40 | 41 | vector row(n + 1); 42 | for (int i = 1; i <= n; ++i) 43 | row[i] = i; 44 | 45 | for (int y = 1; y <= m; ++y) { 46 | row[0] = y; 47 | int best_this_row = row[0]; 48 | 49 | int previous = y - 1; 50 | for (int x = 1; x <= n; ++x) { 51 | int old_row = row[x]; 52 | if (allow_replacements) { 53 | row[x] = min(previous + (s1.str_[y - 1] == s2.str_[x - 1] ? 0 : 1), 54 | min(row[x - 1], row[x]) + 1); 55 | } 56 | else { 57 | if (s1.str_[y - 1] == s2.str_[x - 1]) 58 | row[x] = previous; 59 | else 60 | row[x] = min(row[x - 1], row[x]) + 1; 61 | } 62 | previous = old_row; 63 | best_this_row = min(best_this_row, row[x]); 64 | } 65 | 66 | if (max_edit_distance && best_this_row > max_edit_distance) 67 | return max_edit_distance + 1; 68 | } 69 | 70 | return row[n]; 71 | } 72 | -------------------------------------------------------------------------------- /src/edit_distance.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_EDIT_DISTANCE_H_ 16 | #define NINJA_EDIT_DISTANCE_H_ 17 | 18 | #include "string_piece.h" 19 | 20 | int EditDistance(const StringPiece& s1, 21 | const StringPiece& s2, 22 | bool allow_replacements = true, 23 | int max_edit_distance = 0); 24 | 25 | #endif // NINJA_EDIT_DISTANCE_H_ 26 | -------------------------------------------------------------------------------- /src/edit_distance_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "edit_distance.h" 16 | 17 | #include "test.h" 18 | 19 | TEST(EditDistanceTest, TestEmpty) { 20 | EXPECT_EQ(5, EditDistance("", "ninja")); 21 | EXPECT_EQ(5, EditDistance("ninja", "")); 22 | EXPECT_EQ(0, EditDistance("", "")); 23 | } 24 | 25 | TEST(EditDistanceTest, TestMaxDistance) { 26 | const bool allow_replacements = true; 27 | for (int max_distance = 1; max_distance < 7; ++max_distance) { 28 | EXPECT_EQ(max_distance + 1, 29 | EditDistance("abcdefghijklmnop", "ponmlkjihgfedcba", 30 | allow_replacements, max_distance)); 31 | } 32 | } 33 | 34 | TEST(EditDistanceTest, TestAllowReplacements) { 35 | bool allow_replacements = true; 36 | EXPECT_EQ(1, EditDistance("ninja", "njnja", allow_replacements)); 37 | EXPECT_EQ(1, EditDistance("njnja", "ninja", allow_replacements)); 38 | 39 | allow_replacements = false; 40 | EXPECT_EQ(2, EditDistance("ninja", "njnja", allow_replacements)); 41 | EXPECT_EQ(2, EditDistance("njnja", "ninja", allow_replacements)); 42 | } 43 | 44 | TEST(EditDistanceTest, TestBasics) { 45 | EXPECT_EQ(0, EditDistance("browser_tests", "browser_tests")); 46 | EXPECT_EQ(1, EditDistance("browser_test", "browser_tests")); 47 | EXPECT_EQ(1, EditDistance("browser_tests", "browser_test")); 48 | } 49 | -------------------------------------------------------------------------------- /src/elide_middle.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_ELIDE_MIDDLE_H_ 16 | #define NINJA_ELIDE_MIDDLE_H_ 17 | 18 | #include 19 | #include 20 | 21 | /// Elide the given string @a str with '...' in the middle if the length 22 | /// exceeds @a max_width. Note that this handles ANSI color sequences 23 | /// properly (non-color related sequences are ignored, but using them 24 | /// would wreak the cursor position or terminal state anyway). 25 | void ElideMiddleInPlace(std::string& str, size_t max_width); 26 | 27 | #endif // NINJA_ELIDE_MIDDLE_H_ 28 | -------------------------------------------------------------------------------- /src/elide_middle_perftest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include "elide_middle.h" 23 | #include "metrics.h" 24 | 25 | static const char* kTestInputs[] = { 26 | "01234567890123456789", 27 | "012345\x1B[0;35m67890123456789", 28 | "abcd\x1b[1;31mefg\x1b[0mhlkmnopqrstuvwxyz", 29 | }; 30 | 31 | int main() { 32 | std::vector times; 33 | 34 | int64_t kMaxTimeMillis = 5 * 1000; 35 | int64_t base_time = GetTimeMillis(); 36 | 37 | const int kRuns = 100; 38 | for (int j = 0; j < kRuns; ++j) { 39 | int64_t start = GetTimeMillis(); 40 | if (start >= base_time + kMaxTimeMillis) 41 | break; 42 | 43 | const int kNumRepetitions = 2000; 44 | for (int count = kNumRepetitions; count > 0; --count) { 45 | for (const char* input : kTestInputs) { 46 | size_t input_len = ::strlen(input); 47 | for (size_t max_width = input_len; max_width > 0; --max_width) { 48 | std::string str(input, input_len); 49 | ElideMiddleInPlace(str, max_width); 50 | } 51 | } 52 | } 53 | 54 | int delta = (int)(GetTimeMillis() - start); 55 | times.push_back(delta); 56 | } 57 | 58 | int min = times[0]; 59 | int max = times[0]; 60 | float total = 0; 61 | for (size_t i = 0; i < times.size(); ++i) { 62 | total += times[i]; 63 | if (times[i] < min) 64 | min = times[i]; 65 | else if (times[i] > max) 66 | max = times[i]; 67 | } 68 | 69 | printf("min %dms max %dms avg %.1fms\n", min, max, total / times.size()); 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /src/elide_middle_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "elide_middle.h" 16 | 17 | #include "test.h" 18 | 19 | namespace { 20 | 21 | std::string ElideMiddle(const std::string& str, size_t width) { 22 | std::string result = str; 23 | ElideMiddleInPlace(result, width); 24 | return result; 25 | } 26 | 27 | } // namespace 28 | 29 | 30 | TEST(ElideMiddle, NothingToElide) { 31 | std::string input = "Nothing to elide in this short string."; 32 | EXPECT_EQ(input, ElideMiddle(input, 80)); 33 | EXPECT_EQ(input, ElideMiddle(input, 38)); 34 | EXPECT_EQ("", ElideMiddle(input, 0)); 35 | EXPECT_EQ(".", ElideMiddle(input, 1)); 36 | EXPECT_EQ("..", ElideMiddle(input, 2)); 37 | EXPECT_EQ("...", ElideMiddle(input, 3)); 38 | } 39 | 40 | TEST(ElideMiddle, ElideInTheMiddle) { 41 | std::string input = "01234567890123456789"; 42 | EXPECT_EQ("...9", ElideMiddle(input, 4)); 43 | EXPECT_EQ("0...9", ElideMiddle(input, 5)); 44 | EXPECT_EQ("012...789", ElideMiddle(input, 9)); 45 | EXPECT_EQ("012...6789", ElideMiddle(input, 10)); 46 | EXPECT_EQ("0123...6789", ElideMiddle(input, 11)); 47 | EXPECT_EQ("01234567...23456789", ElideMiddle(input, 19)); 48 | EXPECT_EQ("01234567890123456789", ElideMiddle(input, 20)); 49 | } 50 | 51 | // A few ANSI escape sequences. These macros make the following 52 | // test easier to read and understand. 53 | #define MAGENTA "\x1B[0;35m" 54 | #define NOTHING "\33[m" 55 | #define RED "\x1b[1;31m" 56 | #define RESET "\x1b[0m" 57 | 58 | TEST(ElideMiddle, ElideAnsiEscapeCodes) { 59 | std::string input = "012345" MAGENTA "67890123456789"; 60 | EXPECT_EQ("012..." MAGENTA "6789", ElideMiddle(input, 10)); 61 | EXPECT_EQ("012345" MAGENTA "67...23456789", ElideMiddle(input, 19)); 62 | 63 | EXPECT_EQ("Nothing " NOTHING " string.", 64 | ElideMiddle("Nothing " NOTHING " string.", 18)); 65 | EXPECT_EQ("0" NOTHING "12...6789", 66 | ElideMiddle("0" NOTHING "1234567890123456789", 10)); 67 | 68 | input = "abcd" RED "efg" RESET "hlkmnopqrstuvwxyz"; 69 | EXPECT_EQ("" RED RESET, ElideMiddle(input, 0)); 70 | EXPECT_EQ("." RED RESET, ElideMiddle(input, 1)); 71 | EXPECT_EQ(".." RED RESET, ElideMiddle(input, 2)); 72 | EXPECT_EQ("..." RED RESET, ElideMiddle(input, 3)); 73 | EXPECT_EQ("..." RED RESET "z", ElideMiddle(input, 4)); 74 | EXPECT_EQ("a..." RED RESET "z", ElideMiddle(input, 5)); 75 | EXPECT_EQ("a..." RED RESET "yz", ElideMiddle(input, 6)); 76 | EXPECT_EQ("ab..." RED RESET "yz", ElideMiddle(input, 7)); 77 | EXPECT_EQ("ab..." RED RESET "xyz", ElideMiddle(input, 8)); 78 | EXPECT_EQ("abc..." RED RESET "xyz", ElideMiddle(input, 9)); 79 | EXPECT_EQ("abc..." RED RESET "wxyz", ElideMiddle(input, 10)); 80 | EXPECT_EQ("abcd..." RED RESET "wxyz", ElideMiddle(input, 11)); 81 | EXPECT_EQ("abcd..." RED RESET "vwxyz", ElideMiddle(input, 12)); 82 | 83 | EXPECT_EQ("abcd" RED "ef..." RESET "uvwxyz", ElideMiddle(input, 15)); 84 | EXPECT_EQ("abcd" RED "ef..." RESET "tuvwxyz", ElideMiddle(input, 16)); 85 | EXPECT_EQ("abcd" RED "efg..." RESET "tuvwxyz", ElideMiddle(input, 17)); 86 | EXPECT_EQ("abcd" RED "efg..." RESET "stuvwxyz", ElideMiddle(input, 18)); 87 | EXPECT_EQ("abcd" RED "efg" RESET "h...stuvwxyz", ElideMiddle(input, 19)); 88 | 89 | input = "abcdef" RED "A" RESET "BC"; 90 | EXPECT_EQ("..." RED RESET "C", ElideMiddle(input, 4)); 91 | EXPECT_EQ("a..." RED RESET "C", ElideMiddle(input, 5)); 92 | EXPECT_EQ("a..." RED RESET "BC", ElideMiddle(input, 6)); 93 | EXPECT_EQ("ab..." RED RESET "BC", ElideMiddle(input, 7)); 94 | EXPECT_EQ("ab..." RED "A" RESET "BC", ElideMiddle(input, 8)); 95 | EXPECT_EQ("abcdef" RED "A" RESET "BC", ElideMiddle(input, 9)); 96 | } 97 | 98 | #undef RESET 99 | #undef RED 100 | #undef NOTHING 101 | #undef MAGENTA 102 | -------------------------------------------------------------------------------- /src/eval_env.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_EVAL_ENV_H_ 16 | #define NINJA_EVAL_ENV_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "string_piece.h" 24 | 25 | struct Rule; 26 | 27 | /// An interface for a scope for variable (e.g. "$foo") lookups. 28 | struct Env { 29 | virtual ~Env() {} 30 | virtual std::string LookupVariable(const std::string& var) = 0; 31 | }; 32 | 33 | /// A tokenized string that contains variable references. 34 | /// Can be evaluated relative to an Env. 35 | struct EvalString { 36 | /// @return The evaluated string with variable expanded using value found in 37 | /// environment @a env. 38 | std::string Evaluate(Env* env) const; 39 | 40 | /// @return The string with variables not expanded. 41 | std::string Unparse() const; 42 | 43 | void Clear() { parsed_.clear(); single_token_.clear(); } 44 | bool empty() const { return parsed_.empty() && single_token_.empty(); } 45 | 46 | void AddText(StringPiece text); 47 | void AddSpecial(StringPiece text); 48 | 49 | /// Construct a human-readable representation of the parsed state, 50 | /// for use in tests. 51 | std::string Serialize() const; 52 | 53 | private: 54 | enum TokenType { RAW, SPECIAL }; 55 | typedef std::vector > TokenList; 56 | TokenList parsed_; 57 | 58 | // If we hold only a single RAW token, then we keep it here instead of 59 | // pushing it on TokenList. This saves a bunch of allocations for 60 | // what is a common case. If parsed_ is nonempty, then this value 61 | // must be ignored. 62 | std::string single_token_; 63 | }; 64 | 65 | /// An invocable build command and associated metadata (description, etc.). 66 | struct Rule { 67 | explicit Rule(const std::string& name) : name_(name) {} 68 | 69 | static std::unique_ptr Phony(); 70 | 71 | bool IsPhony() const; 72 | 73 | const std::string& name() const { return name_; } 74 | 75 | void AddBinding(const std::string& key, const EvalString& val); 76 | 77 | static bool IsReservedBinding(const std::string& var); 78 | 79 | const EvalString* GetBinding(const std::string& key) const; 80 | 81 | private: 82 | // Allow the parsers to reach into this object and fill out its fields. 83 | friend struct ManifestParser; 84 | 85 | std::string name_; 86 | typedef std::map Bindings; 87 | Bindings bindings_; 88 | bool phony_ = false; 89 | }; 90 | 91 | /// An Env which contains a mapping of variables to values 92 | /// as well as a pointer to a parent scope. 93 | struct BindingEnv : public Env { 94 | BindingEnv() : parent_(NULL) {} 95 | explicit BindingEnv(BindingEnv* parent) : parent_(parent) {} 96 | 97 | virtual ~BindingEnv() {} 98 | virtual std::string LookupVariable(const std::string& var); 99 | 100 | void AddRule(std::unique_ptr rule); 101 | const Rule* LookupRule(const std::string& rule_name); 102 | const Rule* LookupRuleCurrentScope(const std::string& rule_name); 103 | const std::map>& GetRules() const; 104 | 105 | void AddBinding(const std::string& key, const std::string& val); 106 | 107 | /// This is tricky. Edges want lookup scope to go in this order: 108 | /// 1) value set on edge itself (edge_->env_) 109 | /// 2) value set on rule, with expansion in the edge's scope 110 | /// 3) value set on enclosing scope of edge (edge_->env_->parent_) 111 | /// This function takes as parameters the necessary info to do (2). 112 | std::string LookupWithFallback(const std::string& var, const EvalString* eval, 113 | Env* env); 114 | 115 | private: 116 | std::map bindings_; 117 | std::map> rules_; 118 | BindingEnv* parent_; 119 | }; 120 | 121 | #endif // NINJA_EVAL_ENV_H_ 122 | -------------------------------------------------------------------------------- /src/exit_status.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_EXIT_STATUS_H_ 16 | #define NINJA_EXIT_STATUS_H_ 17 | 18 | // The underlying type of the ExitStatus enum, used to represent a platform-specific 19 | // process exit code. 20 | #ifdef _WIN32 21 | #define EXIT_STATUS_TYPE unsigned long 22 | #else // !_WIN32 23 | #define EXIT_STATUS_TYPE int 24 | #endif // !_WIN32 25 | 26 | 27 | enum ExitStatus : EXIT_STATUS_TYPE { 28 | ExitSuccess=0, 29 | ExitFailure, 30 | ExitInterrupted=130, 31 | }; 32 | 33 | #endif // NINJA_EXIT_STATUS_H_ 34 | -------------------------------------------------------------------------------- /src/explanations.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | /// A class used to record a list of explanation strings associated 25 | /// with a given 'item' pointer. This is used to implement the 26 | /// `-d explain` feature. 27 | struct Explanations { 28 | public: 29 | /// Record an explanation for |item| if this instance is enabled. 30 | void Record(const void* item, const char* fmt, ...) { 31 | va_list args; 32 | va_start(args, fmt); 33 | RecordArgs(item, fmt, args); 34 | va_end(args); 35 | } 36 | 37 | /// Same as Record(), but uses a va_list to pass formatting arguments. 38 | void RecordArgs(const void* item, const char* fmt, va_list args) { 39 | char buffer[1024]; 40 | vsnprintf(buffer, sizeof(buffer), fmt, args); 41 | map_[item].emplace_back(buffer); 42 | } 43 | 44 | /// Lookup the explanations recorded for |item|, and append them 45 | /// to |*out|, if any. 46 | void LookupAndAppend(const void* item, std::vector* out) { 47 | auto it = map_.find(item); 48 | if (it == map_.end()) 49 | return; 50 | 51 | for (const auto& explanation : it->second) 52 | out->push_back(explanation); 53 | } 54 | 55 | private: 56 | std::unordered_map> map_; 57 | }; 58 | 59 | /// Convenience wrapper for an Explanations pointer, which can be null 60 | /// if no explanations need to be recorded. 61 | struct OptionalExplanations { 62 | OptionalExplanations(Explanations* explanations) 63 | : explanations_(explanations) {} 64 | 65 | void Record(const void* item, const char* fmt, ...) { 66 | if (explanations_) { 67 | va_list args; 68 | va_start(args, fmt); 69 | explanations_->RecordArgs(item, fmt, args); 70 | va_end(args); 71 | } 72 | } 73 | 74 | void RecordArgs(const void* item, const char* fmt, va_list args) { 75 | if (explanations_) 76 | explanations_->RecordArgs(item, fmt, args); 77 | } 78 | 79 | void LookupAndAppend(const void* item, std::vector* out) { 80 | if (explanations_) 81 | explanations_->LookupAndAppend(item, out); 82 | } 83 | 84 | Explanations* ptr() const { return explanations_; } 85 | 86 | private: 87 | Explanations* explanations_; 88 | }; 89 | -------------------------------------------------------------------------------- /src/explanations_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "explanations.h" 16 | 17 | #include "test.h" 18 | 19 | namespace { 20 | 21 | const void* MakeItem(size_t v) { 22 | return reinterpret_cast(v); 23 | } 24 | 25 | } // namespace 26 | 27 | TEST(Explanations, Explanations) { 28 | Explanations exp; 29 | 30 | exp.Record(MakeItem(1), "first explanation"); 31 | exp.Record(MakeItem(1), "second explanation"); 32 | exp.Record(MakeItem(2), "third explanation"); 33 | exp.Record(MakeItem(2), "fourth %s", "explanation"); 34 | 35 | std::vector list; 36 | 37 | exp.LookupAndAppend(MakeItem(0), &list); 38 | ASSERT_TRUE(list.empty()); 39 | 40 | exp.LookupAndAppend(MakeItem(1), &list); 41 | ASSERT_EQ(2u, list.size()); 42 | EXPECT_EQ(list[0], "first explanation"); 43 | EXPECT_EQ(list[1], "second explanation"); 44 | 45 | exp.LookupAndAppend(MakeItem(2), &list); 46 | ASSERT_EQ(4u, list.size()); 47 | EXPECT_EQ(list[0], "first explanation"); 48 | EXPECT_EQ(list[1], "second explanation"); 49 | EXPECT_EQ(list[2], "third explanation"); 50 | EXPECT_EQ(list[3], "fourth explanation"); 51 | } 52 | 53 | TEST(Explanations, OptionalExplanationsNonNull) { 54 | Explanations parent; 55 | OptionalExplanations exp(&parent); 56 | 57 | exp.Record(MakeItem(1), "first explanation"); 58 | exp.Record(MakeItem(1), "second explanation"); 59 | exp.Record(MakeItem(2), "third explanation"); 60 | exp.Record(MakeItem(2), "fourth %s", "explanation"); 61 | 62 | std::vector list; 63 | 64 | exp.LookupAndAppend(MakeItem(0), &list); 65 | ASSERT_TRUE(list.empty()); 66 | 67 | exp.LookupAndAppend(MakeItem(1), &list); 68 | ASSERT_EQ(2u, list.size()); 69 | EXPECT_EQ(list[0], "first explanation"); 70 | EXPECT_EQ(list[1], "second explanation"); 71 | 72 | exp.LookupAndAppend(MakeItem(2), &list); 73 | ASSERT_EQ(4u, list.size()); 74 | EXPECT_EQ(list[0], "first explanation"); 75 | EXPECT_EQ(list[1], "second explanation"); 76 | EXPECT_EQ(list[2], "third explanation"); 77 | EXPECT_EQ(list[3], "fourth explanation"); 78 | } 79 | 80 | TEST(Explanations, OptionalExplanationsWithNullPointer) { 81 | OptionalExplanations exp(nullptr); 82 | 83 | exp.Record(MakeItem(1), "first explanation"); 84 | exp.Record(MakeItem(1), "second explanation"); 85 | exp.Record(MakeItem(2), "third explanation"); 86 | exp.Record(MakeItem(2), "fourth %s", "explanation"); 87 | 88 | std::vector list; 89 | exp.LookupAndAppend(MakeItem(0), &list); 90 | ASSERT_TRUE(list.empty()); 91 | 92 | exp.LookupAndAppend(MakeItem(1), &list); 93 | ASSERT_TRUE(list.empty()); 94 | 95 | exp.LookupAndAppend(MakeItem(2), &list); 96 | ASSERT_TRUE(list.empty()); 97 | } 98 | -------------------------------------------------------------------------------- /src/gen_doxygen_mainpage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2011 Google Inc. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | 20 | STATUS=0 21 | 22 | # Print each of its arguments on stderr (one per line) prefixed by the 23 | # basename of this script. 24 | stderr() 25 | { 26 | local me=$(basename "$0") 27 | local i 28 | for i 29 | do 30 | echo >&2 "$me: $i" 31 | done 32 | } 33 | 34 | # Print each of its arguments on stderr (one per line) prefixed by the 35 | # basename of this script and 'error'. 36 | error() 37 | { 38 | local i 39 | for i 40 | do 41 | stderr "error: $i" 42 | done 43 | STATUS=1 44 | } 45 | 46 | generate_header() 47 | { 48 | cat <&2 "usage: $0 inputs..." 82 | exit 1 83 | fi 84 | 85 | generate_header 86 | for i in "$@" 87 | do 88 | include_file "$i" 89 | done 90 | generate_footer 91 | 92 | exit $STATUS 93 | -------------------------------------------------------------------------------- /src/getopt.h: -------------------------------------------------------------------------------- 1 | #ifndef GETOPT_H 2 | #define GETOPT_H 3 | 4 | /* include files needed by this include file */ 5 | 6 | /* macros defined by this include file */ 7 | #define no_argument 0 8 | #define required_argument 1 9 | #define OPTIONAL_ARG 2 10 | 11 | /* types defined by this include file */ 12 | 13 | /* GETOPT_LONG_OPTION_T: The type of long option */ 14 | typedef struct GETOPT_LONG_OPTION_T 15 | { 16 | const char *name; /* the name of the long option */ 17 | int has_arg; /* one of the above macros */ 18 | int *flag; /* determines if getopt_long() returns a 19 | * value for a long option; if it is 20 | * non-NULL, 0 is returned as a function 21 | * value and the value of val is stored in 22 | * the area pointed to by flag. Otherwise, 23 | * val is returned. */ 24 | int val; /* determines the value to return if flag is 25 | * NULL. */ 26 | } GETOPT_LONG_OPTION_T; 27 | 28 | typedef GETOPT_LONG_OPTION_T option; 29 | 30 | #ifdef __cplusplus 31 | extern "C" 32 | { 33 | #endif 34 | 35 | /* externally-defined variables */ 36 | extern char *optarg; 37 | extern int optind; 38 | extern int opterr; 39 | extern int optopt; 40 | 41 | /* function prototypes */ 42 | #ifndef _AIX 43 | int getopt (int argc, char **argv, char *optstring); 44 | #endif 45 | int getopt_long (int argc, char **argv, const char *shortopts, 46 | const GETOPT_LONG_OPTION_T * longopts, int *longind); 47 | int getopt_long_only (int argc, char **argv, const char *shortopts, 48 | const GETOPT_LONG_OPTION_T * longopts, int *longind); 49 | 50 | #ifdef __cplusplus 51 | }; 52 | 53 | #endif 54 | 55 | #endif /* GETOPT_H */ 56 | 57 | /* END OF FILE getopt.h */ 58 | -------------------------------------------------------------------------------- /src/graphviz.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "graphviz.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "dyndep.h" 21 | #include "graph.h" 22 | 23 | using namespace std; 24 | 25 | void GraphViz::AddTarget(Node* node) { 26 | if (visited_nodes_.find(node) != visited_nodes_.end()) 27 | return; 28 | 29 | string pathstr = node->path(); 30 | replace(pathstr.begin(), pathstr.end(), '\\', '/'); 31 | printf("\"%p\" [label=\"%s\"]\n", node, pathstr.c_str()); 32 | visited_nodes_.insert(node); 33 | 34 | Edge* edge = node->in_edge(); 35 | 36 | if (!edge) { 37 | // Leaf node. 38 | // Draw as a rect? 39 | return; 40 | } 41 | 42 | if (visited_edges_.find(edge) != visited_edges_.end()) 43 | return; 44 | visited_edges_.insert(edge); 45 | 46 | if (edge->dyndep_ && edge->dyndep_->dyndep_pending()) { 47 | std::string err; 48 | if (!dyndep_loader_.LoadDyndeps(edge->dyndep_, &err)) { 49 | Warning("%s\n", err.c_str()); 50 | } 51 | } 52 | 53 | if (edge->inputs_.size() == 1 && edge->outputs_.size() == 1) { 54 | // Can draw simply. 55 | // Note extra space before label text -- this is cosmetic and feels 56 | // like a graphviz bug. 57 | printf("\"%p\" -> \"%p\" [label=\" %s\"]\n", 58 | edge->inputs_[0], edge->outputs_[0], edge->rule_->name().c_str()); 59 | } else { 60 | printf("\"%p\" [label=\"%s\", shape=ellipse]\n", 61 | edge, edge->rule_->name().c_str()); 62 | for (vector::iterator out = edge->outputs_.begin(); 63 | out != edge->outputs_.end(); ++out) { 64 | printf("\"%p\" -> \"%p\"\n", edge, *out); 65 | } 66 | for (vector::iterator in = edge->inputs_.begin(); 67 | in != edge->inputs_.end(); ++in) { 68 | const char* order_only = ""; 69 | if (edge->is_order_only(in - edge->inputs_.begin())) 70 | order_only = " style=dotted"; 71 | printf("\"%p\" -> \"%p\" [arrowhead=none%s]\n", (*in), edge, order_only); 72 | } 73 | } 74 | 75 | for (vector::iterator in = edge->inputs_.begin(); 76 | in != edge->inputs_.end(); ++in) { 77 | AddTarget(*in); 78 | } 79 | } 80 | 81 | void GraphViz::Start() { 82 | printf("digraph ninja {\n"); 83 | printf("rankdir=\"LR\"\n"); 84 | printf("node [fontsize=10, shape=box, height=0.25]\n"); 85 | printf("edge [fontsize=10]\n"); 86 | } 87 | 88 | void GraphViz::Finish() { 89 | printf("}\n"); 90 | } 91 | -------------------------------------------------------------------------------- /src/graphviz.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_GRAPHVIZ_H_ 16 | #define NINJA_GRAPHVIZ_H_ 17 | 18 | #include 19 | 20 | #include "dyndep.h" 21 | #include "graph.h" 22 | 23 | struct DiskInterface; 24 | struct Node; 25 | struct Edge; 26 | struct State; 27 | 28 | /// Runs the process of creating GraphViz .dot file output. 29 | struct GraphViz { 30 | GraphViz(State* state, DiskInterface* disk_interface) 31 | : dyndep_loader_(state, disk_interface) {} 32 | void Start(); 33 | void AddTarget(Node* node); 34 | void Finish(); 35 | 36 | DyndepLoader dyndep_loader_; 37 | std::set visited_nodes_; 38 | EdgeSet visited_edges_; 39 | }; 40 | 41 | #endif // NINJA_GRAPHVIZ_H_ 42 | -------------------------------------------------------------------------------- /src/hash_collision_bench.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "build_log.h" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | using namespace std; 23 | 24 | int random(int low, int high) { 25 | return int(low + (rand() / double(RAND_MAX)) * (high - low) + 0.5); 26 | } 27 | 28 | void RandomCommand(char** s) { 29 | int len = random(5, 100); 30 | *s = new char[len+1]; 31 | for (int i = 0; i < len; ++i) 32 | (*s)[i] = (char)random(32, 127); 33 | (*s)[len] = '\0'; 34 | } 35 | 36 | int main() { 37 | const int N = 20 * 1000 * 1000; 38 | 39 | // Leak these, else 10% of the runtime is spent destroying strings. 40 | char** commands = new char*[N]; 41 | pair* hashes = new pair[N]; 42 | 43 | srand((int)time(NULL)); 44 | 45 | for (int i = 0; i < N; ++i) { 46 | RandomCommand(&commands[i]); 47 | hashes[i] = make_pair(BuildLog::LogEntry::HashCommand(commands[i]), i); 48 | } 49 | 50 | sort(hashes, hashes + N); 51 | 52 | int collision_count = 0; 53 | for (int i = 1; i < N; ++i) { 54 | if (hashes[i - 1].first == hashes[i].first) { 55 | if (strcmp(commands[hashes[i - 1].second], 56 | commands[hashes[i].second]) != 0) { 57 | printf("collision!\n string 1: '%s'\n string 2: '%s'\n", 58 | commands[hashes[i - 1].second], 59 | commands[hashes[i].second]); 60 | collision_count++; 61 | } 62 | } 63 | } 64 | printf("\n\n%d collisions after %d runs\n", collision_count, N); 65 | } 66 | -------------------------------------------------------------------------------- /src/hash_map.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_MAP_H_ 16 | #define NINJA_MAP_H_ 17 | 18 | #include 19 | #include 20 | #include "string_piece.h" 21 | #include "util.h" 22 | 23 | #include "third_party/emhash/hash_table8.hpp" 24 | #include "third_party/rapidhash/rapidhash.h" 25 | 26 | namespace std { 27 | template<> 28 | struct hash { 29 | typedef StringPiece argument_type; 30 | typedef size_t result_type; 31 | 32 | size_t operator()(StringPiece key) const { 33 | return rapidhash(key.str_, key.len_); 34 | } 35 | }; 36 | } 37 | 38 | /// A template for hash_maps keyed by a StringPiece whose string is 39 | /// owned externally (typically by the values). Use like: 40 | /// ExternalStringHash::Type foos; to make foos into a hash 41 | /// mapping StringPiece => Foo*. 42 | template 43 | struct ExternalStringHashMap { 44 | typedef emhash8::HashMap Type; 45 | }; 46 | 47 | #endif // NINJA_MAP_H_ 48 | -------------------------------------------------------------------------------- /src/includes_normalize.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef INCLUDES_NORMALIZE_H_ 16 | #define INCLUDES_NORMALIZE_H_ 17 | 18 | #include 19 | #include 20 | 21 | struct StringPiece; 22 | 23 | /// Utility functions for normalizing include paths on Windows. 24 | /// TODO: this likely duplicates functionality of CanonicalizePath; refactor. 25 | struct IncludesNormalize { 26 | /// Normalize path relative to |relative_to|. 27 | IncludesNormalize(const std::string& relative_to); 28 | 29 | // Internal utilities made available for testing, maybe useful otherwise. 30 | static std::string AbsPath(StringPiece s, std::string* err); 31 | static std::string Relativize(StringPiece path, 32 | const std::vector& start_list, 33 | std::string* err); 34 | 35 | /// Normalize by fixing slashes style, fixing redundant .. and . and makes the 36 | /// path |input| relative to |this->relative_to_| and store to |result|. 37 | bool Normalize(const std::string& input, std::string* result, 38 | std::string* err) const; 39 | 40 | private: 41 | std::string relative_to_; 42 | std::vector split_relative_to_; 43 | }; 44 | 45 | #endif // INCLUDES_NORMALIZE_H_ 46 | -------------------------------------------------------------------------------- /src/inline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright 2001 Google Inc. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # This quick script converts a text file into an #include-able header. 18 | # It expects the name of the variable as its first argument, and reads 19 | # stdin and writes stdout. 20 | 21 | varname="$1" 22 | 23 | # 'od' and 'sed' may not be available on all platforms, and may not support the 24 | # flags used here. We must ensure that the script exits with a non-zero exit 25 | # code in those cases. 26 | byte_vals=$(od -t x1 -A n -v) || exit 1 27 | escaped_byte_vals=$(echo "${byte_vals}" \ 28 | | sed -e 's|^[\t ]\{0,\}$||g; s|[\t ]\{1,\}| |g; s| \{1,\}$||g; s| |\\x|g; s|^|"|; s|$|"|') \ 29 | || exit 1 30 | 31 | # Only write output once we have successfully generated the required data 32 | printf "const char %s[] = \n%s;" "${varname}" "${escaped_byte_vals}" 33 | -------------------------------------------------------------------------------- /src/jobserver-posix.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include "jobserver.h" 25 | #include "util.h" 26 | 27 | namespace { 28 | 29 | // Return true if |fd| is a fifo or pipe descriptor. 30 | bool IsFifoDescriptor(int fd) { 31 | struct stat info; 32 | int ret = ::fstat(fd, &info); 33 | return (ret == 0) && ((info.st_mode & S_IFMT) == S_IFIFO); 34 | } 35 | 36 | // Implementation of Jobserver::Client for Posix systems 37 | class PosixJobserverClient : public Jobserver::Client { 38 | public: 39 | virtual ~PosixJobserverClient() { 40 | if (write_fd_ >= 0) 41 | ::close(write_fd_); 42 | if (read_fd_ >= 0) 43 | ::close(read_fd_); 44 | } 45 | 46 | Jobserver::Slot TryAcquire() override { 47 | if (has_implicit_slot_) { 48 | has_implicit_slot_ = false; 49 | return Jobserver::Slot::CreateImplicit(); 50 | } 51 | uint8_t slot_char = '\0'; 52 | int ret; 53 | do { 54 | ret = ::read(read_fd_, &slot_char, 1); 55 | } while (ret < 0 && errno == EINTR); 56 | if (ret == 1) { 57 | return Jobserver::Slot::CreateExplicit(slot_char); 58 | } 59 | return Jobserver::Slot(); 60 | } 61 | 62 | void Release(Jobserver::Slot slot) override { 63 | if (!slot.IsValid()) 64 | return; 65 | 66 | if (slot.IsImplicit()) { 67 | assert(!has_implicit_slot_ && "Implicit slot cannot be released twice!"); 68 | has_implicit_slot_ = true; 69 | return; 70 | } 71 | 72 | uint8_t slot_char = slot.GetExplicitValue(); 73 | int ret; 74 | do { 75 | ret = ::write(write_fd_, &slot_char, 1); 76 | } while (ret < 0 && errno == EINTR); 77 | (void)ret; // Nothing can be done in case of error here. 78 | } 79 | 80 | // Initialize with FIFO file path. 81 | bool InitWithFifo(const std::string& fifo_path, std::string* error) { 82 | if (fifo_path.empty()) { 83 | *error = "Empty fifo path"; 84 | return false; 85 | } 86 | read_fd_ = ::open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC); 87 | if (read_fd_ < 0) { 88 | *error = 89 | std::string("Error opening fifo for reading: ") + strerror(errno); 90 | return false; 91 | } 92 | if (!IsFifoDescriptor(read_fd_)) { 93 | *error = "Not a fifo path: " + fifo_path; 94 | // Let destructor close read_fd_. 95 | return false; 96 | } 97 | write_fd_ = ::open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC); 98 | if (write_fd_ < 0) { 99 | *error = 100 | std::string("Error opening fifo for writing: ") + strerror(errno); 101 | // Let destructor close read_fd_ 102 | return false; 103 | } 104 | return true; 105 | } 106 | 107 | private: 108 | // Set to true if the implicit slot has not been acquired yet. 109 | bool has_implicit_slot_ = true; 110 | 111 | // read and write descriptors. 112 | int read_fd_ = -1; 113 | int write_fd_ = -1; 114 | }; 115 | 116 | } // namespace 117 | 118 | // static 119 | std::unique_ptr Jobserver::Client::Create( 120 | const Jobserver::Config& config, std::string* error) { 121 | bool success = false; 122 | auto client = std::unique_ptr(new PosixJobserverClient); 123 | if (config.mode == Jobserver::Config::kModePosixFifo) { 124 | success = client->InitWithFifo(config.path, error); 125 | } else { 126 | *error = "Unsupported jobserver mode"; 127 | } 128 | if (!success) 129 | client.reset(); 130 | return client; 131 | } 132 | -------------------------------------------------------------------------------- /src/jobserver-win32.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "jobserver.h" 19 | #include "util.h" 20 | 21 | namespace { 22 | 23 | // Implementation of Jobserver::Client for Win32 systems. 24 | // At the moment, only the semaphore scheme is supported, 25 | // even when running under Cygwin which could support the 26 | // pipe version, in theory. 27 | class Win32JobserverClient : public Jobserver::Client { 28 | public: 29 | virtual ~Win32JobserverClient() { 30 | // NOTE: OpenSemaphore() returns NULL on failure. 31 | if (IsValid()) { 32 | ::CloseHandle(handle_); 33 | } 34 | } 35 | 36 | Jobserver::Slot TryAcquire() override { 37 | if (IsValid()) { 38 | if (has_implicit_slot_) { 39 | has_implicit_slot_ = false; 40 | return Jobserver::Slot::CreateImplicit(); 41 | } 42 | 43 | DWORD ret = ::WaitForSingleObject(handle_, 0); 44 | if (ret == WAIT_OBJECT_0) { 45 | // Hard-code value 1 for the explicit slot value. 46 | return Jobserver::Slot::CreateExplicit(1); 47 | } 48 | } 49 | return Jobserver::Slot(); 50 | } 51 | 52 | void Release(Jobserver::Slot slot) override { 53 | if (!slot.IsValid()) 54 | return; 55 | 56 | if (slot.IsImplicit()) { 57 | assert(!has_implicit_slot_ && "Implicit slot cannot be released twice!"); 58 | has_implicit_slot_ = true; 59 | return; 60 | } 61 | 62 | // Nothing can be done in case of error here. 63 | (void)::ReleaseSemaphore(handle_, 1, NULL); 64 | } 65 | 66 | bool InitWithSemaphore(const std::string& name, std::string* error) { 67 | handle_ = ::OpenSemaphoreA(SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, FALSE, 68 | name.c_str()); 69 | if (handle_ == NULL) { 70 | *error = "Error opening semaphore: " + GetLastErrorString(); 71 | return false; 72 | } 73 | return true; 74 | } 75 | 76 | protected: 77 | bool IsValid() const { 78 | // NOTE: OpenSemaphore() returns NULL on failure, not INVALID_HANDLE_VALUE. 79 | return handle_ != NULL; 80 | } 81 | 82 | // Set to true if the implicit slot has not been acquired yet. 83 | bool has_implicit_slot_ = true; 84 | 85 | // Semaphore handle. NULL means not in use. 86 | HANDLE handle_ = NULL; 87 | }; 88 | 89 | } // namespace 90 | 91 | // static 92 | std::unique_ptr Jobserver::Client::Create( 93 | const Jobserver::Config& config, std::string* error) { 94 | bool success = false; 95 | auto client = 96 | std::unique_ptr(new Win32JobserverClient()); 97 | if (config.mode == Jobserver::Config::kModeWin32Semaphore) { 98 | success = client->InitWithSemaphore(config.path, error); 99 | } else { 100 | *error = "Unsupported jobserver mode"; 101 | } 102 | if (!success) 103 | client.reset(); 104 | return client; 105 | } 106 | -------------------------------------------------------------------------------- /src/json.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "json.h" 16 | 17 | #include 18 | #include 19 | 20 | std::string EncodeJSONString(const std::string& in) { 21 | static const char* hex_digits = "0123456789abcdef"; 22 | std::string out; 23 | out.reserve(in.length() * 1.2); 24 | for (std::string::const_iterator it = in.begin(); it != in.end(); ++it) { 25 | char c = *it; 26 | if (c == '\b') 27 | out += "\\b"; 28 | else if (c == '\f') 29 | out += "\\f"; 30 | else if (c == '\n') 31 | out += "\\n"; 32 | else if (c == '\r') 33 | out += "\\r"; 34 | else if (c == '\t') 35 | out += "\\t"; 36 | else if (0x0 <= c && c < 0x20) { 37 | out += "\\u00"; 38 | out += hex_digits[c >> 4]; 39 | out += hex_digits[c & 0xf]; 40 | } else if (c == '\\') 41 | out += "\\\\"; 42 | else if (c == '\"') 43 | out += "\\\""; 44 | else 45 | out += c; 46 | } 47 | return out; 48 | } 49 | 50 | void PrintJSONString(const std::string& in) { 51 | std::string out = EncodeJSONString(in); 52 | fwrite(out.c_str(), 1, out.length(), stdout); 53 | } 54 | -------------------------------------------------------------------------------- /src/json.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_JSON_H_ 16 | #define NINJA_JSON_H_ 17 | 18 | #include 19 | 20 | // Encode a string in JSON format without enclosing quotes 21 | std::string EncodeJSONString(const std::string& in); 22 | 23 | // Print a string in JSON format to stdout without enclosing quotes 24 | void PrintJSONString(const std::string& in); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/json_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "json.h" 16 | 17 | #include "test.h" 18 | 19 | TEST(JSONTest, RegularAscii) { 20 | EXPECT_EQ(EncodeJSONString("foo bar"), "foo bar"); 21 | } 22 | 23 | TEST(JSONTest, EscapedChars) { 24 | EXPECT_EQ(EncodeJSONString("\"\\\b\f\n\r\t"), 25 | "\\\"" 26 | "\\\\" 27 | "\\b\\f\\n\\r\\t"); 28 | } 29 | 30 | // codepoints between 0 and 0x1f should be escaped 31 | TEST(JSONTest, ControlChars) { 32 | EXPECT_EQ(EncodeJSONString("\x01\x1f"), "\\u0001\\u001f"); 33 | } 34 | 35 | // Leave them alone as JSON accepts unicode literals 36 | // out of control character range 37 | TEST(JSONTest, UTF8) { 38 | const char* utf8str = "\xe4\xbd\xa0\xe5\xa5\xbd"; 39 | EXPECT_EQ(EncodeJSONString(utf8str), utf8str); 40 | } 41 | -------------------------------------------------------------------------------- /src/lexer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_LEXER_H_ 16 | #define NINJA_LEXER_H_ 17 | 18 | #include "string_piece.h" 19 | 20 | // Windows may #define ERROR. 21 | #ifdef ERROR 22 | #undef ERROR 23 | #endif 24 | 25 | struct EvalString; 26 | 27 | struct Lexer { 28 | Lexer() {} 29 | /// Helper ctor useful for tests. 30 | explicit Lexer(const char* input); 31 | 32 | enum Token { 33 | ERROR, 34 | BUILD, 35 | COLON, 36 | DEFAULT, 37 | EQUALS, 38 | IDENT, 39 | INCLUDE, 40 | INDENT, 41 | NEWLINE, 42 | PIPE, 43 | PIPE2, 44 | PIPEAT, 45 | POOL, 46 | RULE, 47 | SUBNINJA, 48 | TEOF, 49 | }; 50 | 51 | /// Return a human-readable form of a token, used in error messages. 52 | static const char* TokenName(Token t); 53 | 54 | /// Return a human-readable token hint, used in error messages. 55 | static const char* TokenErrorHint(Token expected); 56 | 57 | /// If the last token read was an ERROR token, provide more info 58 | /// or the empty string. 59 | std::string DescribeLastError(); 60 | 61 | /// Start parsing some input. 62 | void Start(StringPiece filename, StringPiece input); 63 | 64 | /// Read a Token from the Token enum. 65 | Token ReadToken(); 66 | 67 | /// Rewind to the last read Token. 68 | void UnreadToken(); 69 | 70 | /// If the next token is \a token, read it and return true. 71 | bool PeekToken(Token token); 72 | 73 | /// Read a simple identifier (a rule or variable name). 74 | /// Returns false if a name can't be read. 75 | bool ReadIdent(std::string* out); 76 | 77 | /// Read a path (complete with $escapes). 78 | /// Returns false only on error, returned path may be empty if a delimiter 79 | /// (space, newline) is hit. 80 | bool ReadPath(EvalString* path, std::string* err) { 81 | return ReadEvalString(path, true, err); 82 | } 83 | 84 | /// Read the value side of a var = value line (complete with $escapes). 85 | /// Returns false only on error. 86 | bool ReadVarValue(EvalString* value, std::string* err) { 87 | return ReadEvalString(value, false, err); 88 | } 89 | 90 | /// Construct an error message with context. 91 | bool Error(const std::string& message, std::string* err); 92 | 93 | private: 94 | /// Skip past whitespace (called after each read token/ident/etc.). 95 | void EatWhitespace(); 96 | 97 | /// Read a $-escaped string. 98 | bool ReadEvalString(EvalString* eval, bool path, std::string* err); 99 | 100 | StringPiece filename_; 101 | StringPiece input_; 102 | const char* ofs_; 103 | const char* last_token_; 104 | }; 105 | 106 | #endif // NINJA_LEXER_H_ 107 | -------------------------------------------------------------------------------- /src/lexer_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "lexer.h" 16 | 17 | #include "eval_env.h" 18 | #include "test.h" 19 | 20 | using namespace std; 21 | 22 | TEST(Lexer, ReadVarValue) { 23 | Lexer lexer("plain text $var $VaR ${x}\n"); 24 | EvalString eval; 25 | string err; 26 | EXPECT_TRUE(lexer.ReadVarValue(&eval, &err)); 27 | EXPECT_EQ("", err); 28 | EXPECT_EQ("[plain text ][$var][ ][$VaR][ ][$x]", 29 | eval.Serialize()); 30 | } 31 | 32 | TEST(Lexer, ReadEvalStringEscapes) { 33 | Lexer lexer("$ $$ab c$: $\ncde\n"); 34 | EvalString eval; 35 | string err; 36 | EXPECT_TRUE(lexer.ReadVarValue(&eval, &err)); 37 | EXPECT_EQ("", err); 38 | EXPECT_EQ("[ $ab c: cde]", 39 | eval.Serialize()); 40 | } 41 | 42 | TEST(Lexer, ReadIdent) { 43 | Lexer lexer("foo baR baz_123 foo-bar"); 44 | string ident; 45 | EXPECT_TRUE(lexer.ReadIdent(&ident)); 46 | EXPECT_EQ("foo", ident); 47 | EXPECT_TRUE(lexer.ReadIdent(&ident)); 48 | EXPECT_EQ("baR", ident); 49 | EXPECT_TRUE(lexer.ReadIdent(&ident)); 50 | EXPECT_EQ("baz_123", ident); 51 | EXPECT_TRUE(lexer.ReadIdent(&ident)); 52 | EXPECT_EQ("foo-bar", ident); 53 | } 54 | 55 | TEST(Lexer, ReadIdentCurlies) { 56 | // Verify that ReadIdent includes dots in the name, 57 | // but in an expansion $bar.dots stops at the dot. 58 | Lexer lexer("foo.dots $bar.dots ${bar.dots}\n"); 59 | string ident; 60 | EXPECT_TRUE(lexer.ReadIdent(&ident)); 61 | EXPECT_EQ("foo.dots", ident); 62 | 63 | EvalString eval; 64 | string err; 65 | EXPECT_TRUE(lexer.ReadVarValue(&eval, &err)); 66 | EXPECT_EQ("", err); 67 | EXPECT_EQ("[$bar][.dots ][$bar.dots]", 68 | eval.Serialize()); 69 | } 70 | 71 | TEST(Lexer, Error) { 72 | Lexer lexer("foo$\nbad $"); 73 | EvalString eval; 74 | string err; 75 | ASSERT_FALSE(lexer.ReadVarValue(&eval, &err)); 76 | EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" 77 | "bad $\n" 78 | " ^ near here" 79 | , err); 80 | } 81 | 82 | TEST(Lexer, CommentEOF) { 83 | // Verify we don't run off the end of the string when the EOF is 84 | // mid-comment. 85 | Lexer lexer("# foo"); 86 | Lexer::Token token = lexer.ReadToken(); 87 | EXPECT_EQ(Lexer::ERROR, token); 88 | } 89 | 90 | TEST(Lexer, Tabs) { 91 | // Verify we print a useful error on a disallowed character. 92 | Lexer lexer(" \tfoobar"); 93 | Lexer::Token token = lexer.ReadToken(); 94 | EXPECT_EQ(Lexer::INDENT, token); 95 | token = lexer.ReadToken(); 96 | EXPECT_EQ(Lexer::ERROR, token); 97 | EXPECT_EQ("tabs are not allowed, use spaces", lexer.DescribeLastError()); 98 | } 99 | -------------------------------------------------------------------------------- /src/line_printer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_LINE_PRINTER_H_ 16 | #define NINJA_LINE_PRINTER_H_ 17 | 18 | #include 19 | #include 20 | 21 | /// Prints lines of text, possibly overprinting previously printed lines 22 | /// if the terminal supports it. 23 | struct LinePrinter { 24 | LinePrinter(); 25 | 26 | bool is_smart_terminal() const { return smart_terminal_; } 27 | void set_smart_terminal(bool smart) { smart_terminal_ = smart; } 28 | 29 | bool supports_color() const { return supports_color_; } 30 | 31 | enum LineType { 32 | FULL, 33 | ELIDE 34 | }; 35 | /// Overprints the current line. If type is ELIDE, elides to_print to fit on 36 | /// one line. 37 | void Print(std::string to_print, LineType type); 38 | 39 | /// Prints a string on a new line, not overprinting previous output. 40 | void PrintOnNewLine(const std::string& to_print); 41 | 42 | /// Lock or unlock the console. Any output sent to the LinePrinter while the 43 | /// console is locked will not be printed until it is unlocked. 44 | void SetConsoleLocked(bool locked); 45 | 46 | private: 47 | /// Whether we can do fancy terminal control codes. 48 | bool smart_terminal_; 49 | 50 | /// Whether we can use ISO 6429 (ANSI) color sequences. 51 | bool supports_color_; 52 | 53 | /// Whether the caret is at the beginning of a blank line. 54 | bool have_blank_line_; 55 | 56 | /// Whether console is locked. 57 | bool console_locked_; 58 | 59 | /// Buffered current line while console is locked. 60 | std::string line_buffer_; 61 | 62 | /// Buffered line type while console is locked. 63 | LineType line_type_; 64 | 65 | /// Buffered console output while console is locked. 66 | std::string output_buffer_; 67 | 68 | #ifdef _WIN32 69 | void* console_; 70 | #endif 71 | 72 | /// Print the given data to the console, or buffer it if it is locked. 73 | void PrintOrBuffer(const char *data, size_t size); 74 | }; 75 | 76 | #endif // NINJA_LINE_PRINTER_H_ 77 | -------------------------------------------------------------------------------- /src/load_status.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_LOAD_STATUS_H_ 16 | #define NINJA_LOAD_STATUS_H_ 17 | 18 | enum LoadStatus { 19 | LOAD_ERROR, 20 | LOAD_SUCCESS, 21 | LOAD_NOT_FOUND, 22 | }; 23 | 24 | #endif // NINJA_LOAD_STATUS_H_ 25 | -------------------------------------------------------------------------------- /src/manifest_parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_MANIFEST_PARSER_H_ 16 | #define NINJA_MANIFEST_PARSER_H_ 17 | 18 | #include "parser.h" 19 | 20 | #include 21 | #include 22 | 23 | struct BindingEnv; 24 | struct EvalString; 25 | 26 | enum DupeEdgeAction { 27 | kDupeEdgeActionWarn, 28 | kDupeEdgeActionError, 29 | }; 30 | 31 | enum PhonyCycleAction { 32 | kPhonyCycleActionWarn, 33 | kPhonyCycleActionError, 34 | }; 35 | 36 | struct ManifestParserOptions { 37 | PhonyCycleAction phony_cycle_action_ = kPhonyCycleActionWarn; 38 | }; 39 | 40 | /// Parses .ninja files. 41 | struct ManifestParser : public Parser { 42 | ManifestParser(State* state, FileReader* file_reader, 43 | ManifestParserOptions options = ManifestParserOptions()); 44 | 45 | /// Parse a text string of input. Used by tests. 46 | bool ParseTest(const std::string& input, std::string* err) { 47 | quiet_ = true; 48 | return Parse("input", input, err); 49 | } 50 | 51 | private: 52 | /// Parse a file, given its contents as a string. 53 | bool Parse(const std::string& filename, const std::string& input, 54 | std::string* err); 55 | 56 | /// Parse various statement types. 57 | bool ParsePool(std::string* err); 58 | bool ParseRule(std::string* err); 59 | bool ParseLet(std::string* key, EvalString* val, std::string* err); 60 | bool ParseEdge(std::string* err); 61 | bool ParseDefault(std::string* err); 62 | 63 | /// Parse either a 'subninja' or 'include' line. 64 | bool ParseFileInclude(bool new_scope, std::string* err); 65 | 66 | BindingEnv* env_; 67 | ManifestParserOptions options_; 68 | bool quiet_; 69 | 70 | // ins_/out_/validations_ are reused across invocations to ParseEdge(), 71 | // to save on the otherwise constant memory reallocation. 72 | // subparser_ is reused solely to get better reuse out ins_/outs_/validation_. 73 | std::unique_ptr subparser_; 74 | std::vector ins_, outs_, validations_; 75 | }; 76 | 77 | #endif // NINJA_MANIFEST_PARSER_H_ 78 | -------------------------------------------------------------------------------- /src/manifest_parser_perftest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Tests manifest parser performance. Expects to be run in ninja's root 16 | // directory. 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef _WIN32 26 | #include "getopt.h" 27 | #include 28 | #elif defined(_AIX) 29 | #include "getopt.h" 30 | #include 31 | #else 32 | #include 33 | #include 34 | #endif 35 | 36 | #include "disk_interface.h" 37 | #include "graph.h" 38 | #include "manifest_parser.h" 39 | #include "metrics.h" 40 | #include "state.h" 41 | #include "util.h" 42 | 43 | using namespace std; 44 | 45 | bool WriteFakeManifests(const string& dir, string* err) { 46 | RealDiskInterface disk_interface; 47 | TimeStamp mtime = disk_interface.Stat(dir + "/build.ninja", err); 48 | if (mtime != 0) // 0 means that the file doesn't exist yet. 49 | return mtime != -1; 50 | 51 | string command = "python misc/write_fake_manifests.py " + dir; 52 | printf("Creating manifest data..."); fflush(stdout); 53 | int exit_code = system(command.c_str()); 54 | printf("done.\n"); 55 | if (exit_code != 0) 56 | *err = "Failed to run " + command; 57 | return exit_code == 0; 58 | } 59 | 60 | int LoadManifests(bool measure_command_evaluation) { 61 | string err; 62 | RealDiskInterface disk_interface; 63 | State state; 64 | ManifestParser parser(&state, &disk_interface); 65 | if (!parser.Load("build.ninja", &err)) { 66 | fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); 67 | exit(1); 68 | } 69 | // Doing an empty build involves reading the manifest and evaluating all 70 | // commands required for the requested targets. So include command 71 | // evaluation in the perftest by default. 72 | int optimization_guard = 0; 73 | if (measure_command_evaluation) 74 | for (size_t i = 0; i < state.edges_.size(); ++i) 75 | optimization_guard += state.edges_[i]->EvaluateCommand().size(); 76 | return optimization_guard; 77 | } 78 | 79 | int main(int argc, char* argv[]) { 80 | bool measure_command_evaluation = true; 81 | int opt; 82 | while ((opt = getopt(argc, argv, const_cast("fh"))) != -1) { 83 | switch (opt) { 84 | case 'f': 85 | measure_command_evaluation = false; 86 | break; 87 | case 'h': 88 | default: 89 | printf("usage: manifest_parser_perftest\n" 90 | "\n" 91 | "options:\n" 92 | " -f only measure manifest load time, not command evaluation time\n" 93 | ); 94 | return 1; 95 | } 96 | } 97 | 98 | const char kManifestDir[] = "build/manifest_perftest"; 99 | 100 | string err; 101 | if (!WriteFakeManifests(kManifestDir, &err)) { 102 | fprintf(stderr, "Failed to write test data: %s\n", err.c_str()); 103 | return 1; 104 | } 105 | 106 | if (chdir(kManifestDir) < 0) 107 | Fatal("chdir: %s", strerror(errno)); 108 | 109 | const int kNumRepetitions = 5; 110 | vector times; 111 | for (int i = 0; i < kNumRepetitions; ++i) { 112 | int64_t start = GetTimeMillis(); 113 | int optimization_guard = LoadManifests(measure_command_evaluation); 114 | int delta = (int)(GetTimeMillis() - start); 115 | printf("%dms (hash: %x)\n", delta, optimization_guard); 116 | times.push_back(delta); 117 | } 118 | 119 | int min = *min_element(times.begin(), times.end()); 120 | int max = *max_element(times.begin(), times.end()); 121 | float total = accumulate(times.begin(), times.end(), 0.0f); 122 | printf("min %dms max %dms avg %.1fms\n", min, max, total / times.size()); 123 | } 124 | -------------------------------------------------------------------------------- /src/metrics.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "metrics.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include "util.h" 25 | 26 | using namespace std; 27 | 28 | Metrics* g_metrics = NULL; 29 | 30 | namespace { 31 | 32 | /// Compute a platform-specific high-res timer value that fits into an int64. 33 | int64_t HighResTimer() { 34 | auto now = chrono::steady_clock::now(); 35 | return chrono::duration_cast( 36 | now.time_since_epoch()) 37 | .count(); 38 | } 39 | 40 | int64_t TimerToMicros(int64_t dt) { 41 | // dt is in ticks. We want microseconds. 42 | return chrono::duration_cast( 43 | std::chrono::steady_clock::duration{ dt }) 44 | .count(); 45 | } 46 | 47 | int64_t TimerToMicros(double dt) { 48 | // dt is in ticks. We want microseconds. 49 | using DoubleSteadyClock = 50 | std::chrono::duration; 51 | return chrono::duration_cast(DoubleSteadyClock{ dt }) 52 | .count(); 53 | } 54 | 55 | } // anonymous namespace 56 | 57 | ScopedMetric::ScopedMetric(Metric* metric) { 58 | metric_ = metric; 59 | if (!metric_) 60 | return; 61 | start_ = HighResTimer(); 62 | } 63 | ScopedMetric::~ScopedMetric() { 64 | if (!metric_) 65 | return; 66 | metric_->count++; 67 | // Leave in the timer's natural frequency to avoid paying the conversion cost 68 | // on every measurement. 69 | int64_t dt = HighResTimer() - start_; 70 | metric_->sum += dt; 71 | } 72 | 73 | Metric* Metrics::NewMetric(const string& name) { 74 | Metric* metric = new Metric; 75 | metric->name = name; 76 | metric->count = 0; 77 | metric->sum = 0; 78 | metrics_.push_back(metric); 79 | return metric; 80 | } 81 | 82 | void Metrics::Report() { 83 | int width = 0; 84 | for (vector::iterator i = metrics_.begin(); 85 | i != metrics_.end(); ++i) { 86 | width = max((int)(*i)->name.size(), width); 87 | } 88 | 89 | printf("%-*s\t%-6s\t%-9s\t%s\n", width, 90 | "metric", "count", "avg (us)", "total (ms)"); 91 | for (vector::iterator i = metrics_.begin(); 92 | i != metrics_.end(); ++i) { 93 | Metric* metric = *i; 94 | uint64_t micros = TimerToMicros(metric->sum); 95 | double total = micros / (double)1000; 96 | double avg = micros / (double)metric->count; 97 | printf("%-*s\t%-6d\t%-8.1f\t%.1f\n", width, metric->name.c_str(), 98 | metric->count, avg, total); 99 | } 100 | } 101 | 102 | double Stopwatch::Elapsed() const { 103 | // Convert to micros after converting to double to minimize error. 104 | return 1e-6 * TimerToMicros(static_cast(NowRaw() - started_)); 105 | } 106 | 107 | uint64_t Stopwatch::NowRaw() const { 108 | return HighResTimer(); 109 | } 110 | 111 | int64_t GetTimeMillis() { 112 | return TimerToMicros(HighResTimer()) / 1000; 113 | } 114 | -------------------------------------------------------------------------------- /src/metrics.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_METRICS_H_ 16 | #define NINJA_METRICS_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "util.h" // For int64_t. 22 | 23 | /// The Metrics module is used for the debug mode that dumps timing stats of 24 | /// various actions. To use, see METRIC_RECORD below. 25 | 26 | /// A single metrics we're tracking, like "depfile load time". 27 | struct Metric { 28 | std::string name; 29 | /// Number of times we've hit the code path. 30 | int count; 31 | /// Total time (in platform-dependent units) we've spent on the code path. 32 | int64_t sum; 33 | }; 34 | 35 | /// A scoped object for recording a metric across the body of a function. 36 | /// Used by the METRIC_RECORD macro. 37 | struct ScopedMetric { 38 | explicit ScopedMetric(Metric* metric); 39 | ~ScopedMetric(); 40 | 41 | private: 42 | Metric* metric_; 43 | /// Timestamp when the measurement started. 44 | /// Value is platform-dependent. 45 | int64_t start_; 46 | }; 47 | 48 | /// The singleton that stores metrics and prints the report. 49 | struct Metrics { 50 | Metric* NewMetric(const std::string& name); 51 | 52 | /// Print a summary report to stdout. 53 | void Report(); 54 | 55 | private: 56 | std::vector metrics_; 57 | }; 58 | 59 | /// Get the current time as relative to some epoch. 60 | /// Epoch varies between platforms; only useful for measuring elapsed time. 61 | int64_t GetTimeMillis(); 62 | 63 | /// A simple stopwatch which returns the time 64 | /// in seconds since Restart() was called. 65 | struct Stopwatch { 66 | public: 67 | Stopwatch() : started_(0) {} 68 | 69 | /// Seconds since Restart() call. 70 | double Elapsed() const; 71 | 72 | void Restart() { started_ = NowRaw(); } 73 | 74 | private: 75 | uint64_t started_; 76 | // Return the current time using the native frequency of the high resolution 77 | // timer. 78 | uint64_t NowRaw() const; 79 | }; 80 | 81 | /// The primary interface to metrics. Use METRIC_RECORD("foobar") at the top 82 | /// of a function to get timing stats recorded for each call of the function. 83 | #define METRIC_RECORD(name) \ 84 | static Metric* metrics_h_metric = \ 85 | g_metrics ? g_metrics->NewMetric(name) : NULL; \ 86 | ScopedMetric metrics_h_scoped(metrics_h_metric); 87 | 88 | /// A variant of METRIC_RECORD that doesn't record anything if |condition| 89 | /// is false. 90 | #define METRIC_RECORD_IF(name, condition) \ 91 | static Metric* metrics_h_metric = \ 92 | g_metrics ? g_metrics->NewMetric(name) : NULL; \ 93 | ScopedMetric metrics_h_scoped((condition) ? metrics_h_metric : NULL); 94 | 95 | extern Metrics* g_metrics; 96 | 97 | #endif // NINJA_METRICS_H_ 98 | -------------------------------------------------------------------------------- /src/minidump-win32.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifdef _MSC_VER 16 | 17 | #include 18 | #include 19 | 20 | #include "util.h" 21 | 22 | using namespace std; 23 | 24 | typedef BOOL (WINAPI *MiniDumpWriteDumpFunc) ( 25 | IN HANDLE, 26 | IN DWORD, 27 | IN HANDLE, 28 | IN MINIDUMP_TYPE, 29 | IN CONST PMINIDUMP_EXCEPTION_INFORMATION, OPTIONAL 30 | IN CONST PMINIDUMP_USER_STREAM_INFORMATION, OPTIONAL 31 | IN CONST PMINIDUMP_CALLBACK_INFORMATION OPTIONAL 32 | ); 33 | 34 | /// Creates a windows minidump in temp folder. 35 | void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep) { 36 | char temp_path[MAX_PATH]; 37 | GetTempPathA(sizeof(temp_path), temp_path); 38 | char temp_file[MAX_PATH]; 39 | sprintf(temp_file, "%s\\ninja_crash_dump_%lu.dmp", 40 | temp_path, GetCurrentProcessId()); 41 | 42 | // Delete any previous minidump of the same name. 43 | DeleteFileA(temp_file); 44 | 45 | // Load DbgHelp.dll dynamically, as library is not present on all 46 | // Windows versions. 47 | HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); 48 | if (dbghelp == NULL) { 49 | Error("failed to create minidump: LoadLibrary('dbghelp.dll'): %s", 50 | GetLastErrorString().c_str()); 51 | return; 52 | } 53 | 54 | MiniDumpWriteDumpFunc mini_dump_write_dump = FunctionCast 55 | (GetProcAddress(dbghelp, "MiniDumpWriteDump")); 56 | if (mini_dump_write_dump == NULL) { 57 | Error("failed to create minidump: GetProcAddress('MiniDumpWriteDump'): %s", 58 | GetLastErrorString().c_str()); 59 | return; 60 | } 61 | 62 | HANDLE hFile = CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, 63 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 64 | if (hFile == NULL) { 65 | Error("failed to create minidump: CreateFileA(%s): %s", 66 | temp_file, GetLastErrorString().c_str()); 67 | return; 68 | } 69 | 70 | MINIDUMP_EXCEPTION_INFORMATION mdei; 71 | mdei.ThreadId = GetCurrentThreadId(); 72 | mdei.ExceptionPointers = pep; 73 | mdei.ClientPointers = FALSE; 74 | MINIDUMP_TYPE mdt = (MINIDUMP_TYPE) (MiniDumpWithDataSegs | 75 | MiniDumpWithHandleData); 76 | 77 | BOOL rv = mini_dump_write_dump(GetCurrentProcess(), GetCurrentProcessId(), 78 | hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0); 79 | CloseHandle(hFile); 80 | 81 | if (!rv) { 82 | Error("MiniDumpWriteDump failed: %s", GetLastErrorString().c_str()); 83 | return; 84 | } 85 | 86 | Warning("minidump created: %s", temp_file); 87 | } 88 | 89 | #endif // _MSC_VER 90 | -------------------------------------------------------------------------------- /src/missing_deps.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_MISSING_DEPS_H_ 16 | #define NINJA_MISSING_DEPS_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | struct DepsLog; 25 | struct DiskInterface; 26 | struct Edge; 27 | struct Node; 28 | struct Rule; 29 | struct State; 30 | 31 | class MissingDependencyScannerDelegate { 32 | public: 33 | virtual ~MissingDependencyScannerDelegate(); 34 | virtual void OnMissingDep(Node* node, const std::string& path, 35 | const Rule& generator) = 0; 36 | }; 37 | 38 | class MissingDependencyPrinter : public MissingDependencyScannerDelegate { 39 | void OnMissingDep(Node* node, const std::string& path, const Rule& generator); 40 | void OnStats(int nodes_processed, int nodes_missing_deps, 41 | int missing_dep_path_count, int generated_nodes, 42 | int generator_rules); 43 | }; 44 | 45 | struct MissingDependencyScanner { 46 | public: 47 | MissingDependencyScanner(MissingDependencyScannerDelegate* delegate, 48 | DepsLog* deps_log, State* state, 49 | DiskInterface* disk_interface); 50 | void ProcessNode(Node* node); 51 | void PrintStats(); 52 | bool HadMissingDeps() { return !nodes_missing_deps_.empty(); } 53 | 54 | void ProcessNodeDeps(Node* node, Node** dep_nodes, int dep_nodes_count); 55 | 56 | bool PathExistsBetween(Edge* from, Edge* to); 57 | 58 | MissingDependencyScannerDelegate* delegate_; 59 | DepsLog* deps_log_; 60 | State* state_; 61 | DiskInterface* disk_interface_; 62 | std::set seen_; 63 | std::set nodes_missing_deps_; 64 | std::set generated_nodes_; 65 | std::set generator_rules_; 66 | int missing_dep_path_count_; 67 | 68 | private: 69 | using InnerAdjacencyMap = std::unordered_map; 70 | using AdjacencyMap = std::unordered_map; 71 | AdjacencyMap adjacency_map_; 72 | }; 73 | 74 | #endif // NINJA_MISSING_DEPS_H_ 75 | -------------------------------------------------------------------------------- /src/msvc_helper-win32.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "msvc_helper.h" 16 | 17 | #include 18 | 19 | #include "util.h" 20 | 21 | using namespace std; 22 | 23 | namespace { 24 | 25 | string Replace(const string& input, const string& find, const string& replace) { 26 | string result = input; 27 | size_t start_pos = 0; 28 | while ((start_pos = result.find(find, start_pos)) != string::npos) { 29 | result.replace(start_pos, find.length(), replace); 30 | start_pos += replace.length(); 31 | } 32 | return result; 33 | } 34 | 35 | } // anonymous namespace 36 | 37 | string EscapeForDepfile(const string& path) { 38 | // Depfiles don't escape single \. 39 | return Replace(path, " ", "\\ "); 40 | } 41 | 42 | int CLWrapper::Run(const string& command, string* output) { 43 | SECURITY_ATTRIBUTES security_attributes = {}; 44 | security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); 45 | security_attributes.bInheritHandle = TRUE; 46 | 47 | // Must be inheritable so subprocesses can dup to children. 48 | HANDLE nul = 49 | CreateFileA("NUL", GENERIC_READ, 50 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 51 | &security_attributes, OPEN_EXISTING, 0, NULL); 52 | if (nul == INVALID_HANDLE_VALUE) 53 | Fatal("couldn't open nul"); 54 | 55 | HANDLE stdout_read, stdout_write; 56 | if (!CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)) 57 | Win32Fatal("CreatePipe"); 58 | 59 | if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) 60 | Win32Fatal("SetHandleInformation"); 61 | 62 | PROCESS_INFORMATION process_info = {}; 63 | STARTUPINFOA startup_info = {}; 64 | startup_info.cb = sizeof(STARTUPINFOA); 65 | startup_info.hStdInput = nul; 66 | startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); 67 | startup_info.hStdOutput = stdout_write; 68 | startup_info.dwFlags |= STARTF_USESTDHANDLES; 69 | 70 | if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL, 71 | /* inherit handles */ TRUE, 0, 72 | env_block_, NULL, 73 | &startup_info, &process_info)) { 74 | Win32Fatal("CreateProcess"); 75 | } 76 | 77 | if (!CloseHandle(nul) || 78 | !CloseHandle(stdout_write)) { 79 | Win32Fatal("CloseHandle"); 80 | } 81 | 82 | // Read all output of the subprocess. 83 | DWORD read_len = 1; 84 | while (read_len) { 85 | char buf[64 << 10]; 86 | read_len = 0; 87 | if (!::ReadFile(stdout_read, buf, sizeof(buf), &read_len, NULL) && 88 | GetLastError() != ERROR_BROKEN_PIPE) { 89 | Win32Fatal("ReadFile"); 90 | } 91 | output->append(buf, read_len); 92 | } 93 | 94 | // Wait for it to exit and grab its exit code. 95 | if (WaitForSingleObject(process_info.hProcess, INFINITE) == WAIT_FAILED) 96 | Win32Fatal("WaitForSingleObject"); 97 | DWORD exit_code = 0; 98 | if (!GetExitCodeProcess(process_info.hProcess, &exit_code)) 99 | Win32Fatal("GetExitCodeProcess"); 100 | 101 | if (!CloseHandle(stdout_read) || 102 | !CloseHandle(process_info.hProcess) || 103 | !CloseHandle(process_info.hThread)) { 104 | Win32Fatal("CloseHandle"); 105 | } 106 | 107 | return exit_code; 108 | } 109 | -------------------------------------------------------------------------------- /src/msvc_helper.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef MSVC_HELPER_H_ 16 | #define MSVC_HELPER_H_ 17 | 18 | #include 19 | 20 | std::string EscapeForDepfile(const std::string& path); 21 | 22 | /// Wraps a synchronous execution of a CL subprocess. 23 | struct CLWrapper { 24 | CLWrapper() : env_block_(NULL) {} 25 | 26 | /// Set the environment block (as suitable for CreateProcess) to be used 27 | /// by Run(). 28 | void SetEnvBlock(void* env_block) { env_block_ = env_block; } 29 | 30 | /// Start a process and gather its raw output. Returns its exit code. 31 | /// Crashes (calls Fatal()) on error. 32 | int Run(const std::string& command, std::string* output); 33 | 34 | void* env_block_; 35 | }; 36 | 37 | #endif // MSVC_HELPER_H_ 38 | -------------------------------------------------------------------------------- /src/msvc_helper_main-win32.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "msvc_helper.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "clparser.h" 23 | #include "util.h" 24 | 25 | #include "getopt.h" 26 | 27 | using namespace std; 28 | 29 | namespace { 30 | 31 | void Usage() { 32 | printf( 33 | "usage: ninja -t msvc [options] -- cl.exe /showIncludes /otherArgs\n" 34 | "options:\n" 35 | " -e ENVFILE load environment block from ENVFILE as environment\n" 36 | " -o FILE write output dependency information to FILE.d\n" 37 | " -p STRING localized prefix of msvc's /showIncludes output\n" 38 | ); 39 | } 40 | 41 | void PushPathIntoEnvironment(const string& env_block) { 42 | const char* as_str = env_block.c_str(); 43 | while (as_str[0]) { 44 | if (_strnicmp(as_str, "path=", 5) == 0) { 45 | _putenv(as_str); 46 | return; 47 | } else { 48 | as_str = &as_str[strlen(as_str) + 1]; 49 | } 50 | } 51 | } 52 | 53 | void WriteDepFileOrDie(const char* object_path, const CLParser& parse) { 54 | string depfile_path = string(object_path) + ".d"; 55 | FILE* depfile = fopen(depfile_path.c_str(), "w"); 56 | if (!depfile) { 57 | platformAwareUnlink(object_path); 58 | Fatal("opening %s: %s", depfile_path.c_str(), 59 | GetLastErrorString().c_str()); 60 | } 61 | if (fprintf(depfile, "%s: ", object_path) < 0) { 62 | platformAwareUnlink(object_path); 63 | fclose(depfile); 64 | platformAwareUnlink(depfile_path.c_str()); 65 | Fatal("writing %s", depfile_path.c_str()); 66 | } 67 | const set& headers = parse.includes_; 68 | for (set::const_iterator i = headers.begin(); 69 | i != headers.end(); ++i) { 70 | if (fprintf(depfile, "%s\n", EscapeForDepfile(*i).c_str()) < 0) { 71 | platformAwareUnlink(object_path); 72 | fclose(depfile); 73 | platformAwareUnlink(depfile_path.c_str()); 74 | Fatal("writing %s", depfile_path.c_str()); 75 | } 76 | } 77 | fclose(depfile); 78 | } 79 | 80 | } // anonymous namespace 81 | 82 | int MSVCHelperMain(int argc, char** argv) { 83 | const char* output_filename = NULL; 84 | const char* envfile = NULL; 85 | 86 | const option kLongOptions[] = { 87 | { "help", no_argument, NULL, 'h' }, 88 | { NULL, 0, NULL, 0 } 89 | }; 90 | int opt; 91 | string deps_prefix; 92 | while ((opt = getopt_long(argc, argv, "e:o:p:h", kLongOptions, NULL)) != -1) { 93 | switch (opt) { 94 | case 'e': 95 | envfile = optarg; 96 | break; 97 | case 'o': 98 | output_filename = optarg; 99 | break; 100 | case 'p': 101 | deps_prefix = optarg; 102 | break; 103 | case 'h': 104 | default: 105 | Usage(); 106 | return 0; 107 | } 108 | } 109 | 110 | string env; 111 | if (envfile) { 112 | string err; 113 | if (ReadFile(envfile, &env, &err) != 0) 114 | Fatal("couldn't open %s: %s", envfile, err.c_str()); 115 | PushPathIntoEnvironment(env); 116 | } 117 | 118 | char* command = GetCommandLineA(); 119 | command = strstr(command, " -- "); 120 | if (!command) { 121 | Fatal("expected command line to end with \" -- command args\""); 122 | } 123 | command += 4; 124 | 125 | CLWrapper cl; 126 | if (!env.empty()) 127 | cl.SetEnvBlock((void*)env.data()); 128 | string output; 129 | int exit_code = cl.Run(command, &output); 130 | 131 | if (output_filename) { 132 | CLParser parser; 133 | string err; 134 | if (!parser.Parse(output, deps_prefix, &output, &err)) 135 | Fatal("%s\n", err.c_str()); 136 | WriteDepFileOrDie(output_filename, parser); 137 | } 138 | 139 | if (output.empty()) 140 | return exit_code; 141 | 142 | // CLWrapper's output already as \r\n line endings, make sure the C runtime 143 | // doesn't expand this to \r\r\n. 144 | _setmode(_fileno(stdout), _O_BINARY); 145 | // Avoid printf and C strings, since the actual output might contain null 146 | // bytes like UTF-16 does (yuck). 147 | fwrite(&output[0], 1, output.size(), stdout); 148 | 149 | return exit_code; 150 | } 151 | -------------------------------------------------------------------------------- /src/msvc_helper_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "msvc_helper.h" 16 | 17 | #include "test.h" 18 | #include "util.h" 19 | 20 | using namespace std; 21 | 22 | TEST(EscapeForDepfileTest, SpacesInFilename) { 23 | ASSERT_EQ("sub\\some\\ sdk\\foo.h", 24 | EscapeForDepfile("sub\\some sdk\\foo.h")); 25 | } 26 | 27 | TEST(MSVCHelperTest, EnvBlock) { 28 | char env_block[] = "foo=bar\0"; 29 | CLWrapper cl; 30 | cl.SetEnvBlock(env_block); 31 | string output; 32 | cl.Run("cmd /c \"echo foo is %foo%", &output); 33 | ASSERT_EQ("foo is bar\r\n", output); 34 | } 35 | 36 | TEST(MSVCHelperTest, NoReadOfStderr) { 37 | CLWrapper cl; 38 | string output; 39 | cl.Run("cmd /c \"echo to stdout&& echo to stderr 1>&2", &output); 40 | ASSERT_EQ("to stdout\r\n", output); 41 | } 42 | -------------------------------------------------------------------------------- /src/ninja_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | int main(int argc, char **argv) { 18 | testing::InitGoogleTest(&argc, argv); 19 | return RUN_ALL_TESTS(); 20 | } 21 | -------------------------------------------------------------------------------- /src/parser.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "parser.h" 16 | 17 | #include "disk_interface.h" 18 | #include "metrics.h" 19 | 20 | using namespace std; 21 | 22 | bool Parser::Load(const string& filename, string* err, Lexer* parent) { 23 | // If |parent| is not NULL, metrics collection has been started by a parent 24 | // Parser::Load() in our call stack. Do not start a new one here to avoid 25 | // over-counting parsing times. 26 | METRIC_RECORD_IF(".ninja parse", parent == NULL); 27 | string contents; 28 | string read_err; 29 | if (file_reader_->ReadFile(filename, &contents, &read_err) != 30 | FileReader::Okay) { 31 | *err = "loading '" + filename + "': " + read_err; 32 | if (parent) 33 | parent->Error(string(*err), err); 34 | return false; 35 | } 36 | 37 | return Parse(filename, contents, err); 38 | } 39 | 40 | bool Parser::ExpectToken(Lexer::Token expected, string* err) { 41 | Lexer::Token token = lexer_.ReadToken(); 42 | if (token != expected) { 43 | string message = string("expected ") + Lexer::TokenName(expected); 44 | message += string(", got ") + Lexer::TokenName(token); 45 | message += Lexer::TokenErrorHint(expected); 46 | return lexer_.Error(message, err); 47 | } 48 | return true; 49 | } 50 | -------------------------------------------------------------------------------- /src/parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_PARSER_H_ 16 | #define NINJA_PARSER_H_ 17 | 18 | #include 19 | 20 | #include "lexer.h" 21 | 22 | struct FileReader; 23 | struct State; 24 | 25 | /// Base class for parsers. 26 | struct Parser { 27 | Parser(State* state, FileReader* file_reader) 28 | : state_(state), file_reader_(file_reader) {} 29 | virtual ~Parser() {} 30 | 31 | /// Load and parse a file. 32 | bool Load(const std::string& filename, std::string* err, Lexer* parent = NULL); 33 | 34 | protected: 35 | /// If the next token is not \a expected, produce an error string 36 | /// saying "expected foo, got bar". 37 | bool ExpectToken(Lexer::Token expected, std::string* err); 38 | 39 | State* state_; 40 | FileReader* file_reader_; 41 | Lexer lexer_; 42 | 43 | private: 44 | /// Parse a file, given its contents as a string. 45 | virtual bool Parse(const std::string& filename, const std::string& input, 46 | std::string* err) = 0; 47 | }; 48 | 49 | #endif // NINJA_PARSER_H_ 50 | -------------------------------------------------------------------------------- /src/real_command_runner.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "build.h" 16 | #include "jobserver.h" 17 | #include "limits.h" 18 | #include "subprocess.h" 19 | 20 | struct RealCommandRunner : public CommandRunner { 21 | explicit RealCommandRunner(const BuildConfig& config, 22 | Jobserver::Client* jobserver) 23 | : config_(config), jobserver_(jobserver) {} 24 | size_t CanRunMore() const override; 25 | bool StartCommand(Edge* edge) override; 26 | bool WaitForCommand(Result* result) override; 27 | std::vector GetActiveEdges() override; 28 | void Abort() override; 29 | 30 | void ClearJobTokens() { 31 | if (jobserver_) { 32 | for (Edge* edge : GetActiveEdges()) { 33 | jobserver_->Release(std::move(edge->job_slot_)); 34 | } 35 | } 36 | } 37 | 38 | const BuildConfig& config_; 39 | SubprocessSet subprocs_; 40 | Jobserver::Client* jobserver_ = nullptr; 41 | std::map subproc_to_edge_; 42 | }; 43 | 44 | std::vector RealCommandRunner::GetActiveEdges() { 45 | std::vector edges; 46 | for (std::map::iterator e = 47 | subproc_to_edge_.begin(); 48 | e != subproc_to_edge_.end(); ++e) 49 | edges.push_back(e->second); 50 | return edges; 51 | } 52 | 53 | void RealCommandRunner::Abort() { 54 | ClearJobTokens(); 55 | subprocs_.Clear(); 56 | } 57 | 58 | size_t RealCommandRunner::CanRunMore() const { 59 | size_t subproc_number = 60 | subprocs_.running_.size() + subprocs_.finished_.size(); 61 | 62 | int64_t capacity = config_.parallelism - subproc_number; 63 | 64 | if (jobserver_) { 65 | // When a jobserver token pool is used, make the 66 | // capacity infinite, and let FindWork() limit jobs 67 | // through token acquisitions instead. 68 | capacity = INT_MAX; 69 | } 70 | 71 | if (config_.max_load_average > 0.0f) { 72 | int load_capacity = config_.max_load_average - GetLoadAverage(); 73 | if (load_capacity < capacity) 74 | capacity = load_capacity; 75 | } 76 | 77 | if (capacity < 0) 78 | capacity = 0; 79 | 80 | if (capacity == 0 && subprocs_.running_.empty()) 81 | // Ensure that we make progress. 82 | capacity = 1; 83 | 84 | return capacity; 85 | } 86 | 87 | bool RealCommandRunner::StartCommand(Edge* edge) { 88 | std::string command = edge->EvaluateCommand(); 89 | Subprocess* subproc = subprocs_.Add(command, edge->use_console()); 90 | if (!subproc) 91 | return false; 92 | subproc_to_edge_.insert(std::make_pair(subproc, edge)); 93 | 94 | return true; 95 | } 96 | 97 | bool RealCommandRunner::WaitForCommand(Result* result) { 98 | Subprocess* subproc; 99 | while ((subproc = subprocs_.NextFinished()) == NULL) { 100 | bool interrupted = subprocs_.DoWork(); 101 | if (interrupted) 102 | return false; 103 | } 104 | 105 | result->status = subproc->Finish(); 106 | result->output = subproc->GetOutput(); 107 | 108 | std::map::iterator e = 109 | subproc_to_edge_.find(subproc); 110 | result->edge = e->second; 111 | subproc_to_edge_.erase(e); 112 | 113 | delete subproc; 114 | return true; 115 | } 116 | 117 | CommandRunner* CommandRunner::factory(const BuildConfig& config, 118 | Jobserver::Client* jobserver) { 119 | return new RealCommandRunner(config, jobserver); 120 | } 121 | -------------------------------------------------------------------------------- /src/state_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "graph.h" 16 | #include "state.h" 17 | #include "test.h" 18 | 19 | using namespace std; 20 | 21 | namespace { 22 | 23 | TEST(State, Basic) { 24 | State state; 25 | 26 | EvalString command; 27 | command.AddText("cat "); 28 | command.AddSpecial("in"); 29 | command.AddText(" > "); 30 | command.AddSpecial("out"); 31 | 32 | Rule* rule = new Rule("cat"); 33 | rule->AddBinding("command", command); 34 | state.bindings_.AddRule(std::unique_ptr(rule)); 35 | 36 | Edge* edge = state.AddEdge(rule); 37 | state.AddIn(edge, "in1", 0); 38 | state.AddIn(edge, "in2", 0); 39 | state.AddOut(edge, "out", 0, nullptr); 40 | 41 | EXPECT_EQ("cat in1 in2 > out", edge->EvaluateCommand()); 42 | 43 | EXPECT_FALSE(state.GetNode("in1", 0)->dirty()); 44 | EXPECT_FALSE(state.GetNode("in2", 0)->dirty()); 45 | EXPECT_FALSE(state.GetNode("out", 0)->dirty()); 46 | } 47 | 48 | } // namespace 49 | -------------------------------------------------------------------------------- /src/status.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_STATUS_H_ 16 | #define NINJA_STATUS_H_ 17 | 18 | #include 19 | #include "exit_status.h" 20 | 21 | struct BuildConfig; 22 | struct Edge; 23 | struct Explanations; 24 | 25 | /// Abstract interface to object that tracks the status of a build: 26 | /// completion fraction, printing updates. 27 | struct Status { 28 | virtual void EdgeAddedToPlan(const Edge* edge) = 0; 29 | virtual void EdgeRemovedFromPlan(const Edge* edge) = 0; 30 | virtual void BuildEdgeStarted(const Edge* edge, 31 | int64_t start_time_millis) = 0; 32 | virtual void BuildEdgeFinished(Edge* edge, int64_t start_time_millis, 33 | int64_t end_time_millis, ExitStatus exit_code, 34 | const std::string& output) = 0; 35 | virtual void BuildStarted() = 0; 36 | virtual void BuildFinished() = 0; 37 | 38 | /// Set the Explanations instance to use to report explanations, 39 | /// argument can be nullptr if no explanations need to be printed 40 | /// (which is the default). 41 | virtual void SetExplanations(Explanations*) = 0; 42 | 43 | virtual void Info(const char* msg, ...) = 0; 44 | virtual void Warning(const char* msg, ...) = 0; 45 | virtual void Error(const char* msg, ...) = 0; 46 | 47 | virtual ~Status() { } 48 | 49 | /// creates the actual implementation 50 | static Status* factory(const BuildConfig&); 51 | }; 52 | 53 | #endif // NINJA_STATUS_H_ 54 | -------------------------------------------------------------------------------- /src/status_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "status.h" 16 | 17 | #include "test.h" 18 | 19 | TEST(StatusTest, StatusFormatElapsed) { 20 | BuildConfig config; 21 | StatusPrinter status(config); 22 | 23 | status.BuildStarted(); 24 | // Before any task is done, the elapsed time must be zero. 25 | EXPECT_EQ("[%/e0.000]", status.FormatProgressStatus("[%%/e%e]", 0)); 26 | // Before any task is done, the elapsed time must be zero. 27 | EXPECT_EQ("[%/e00:00]", status.FormatProgressStatus("[%%/e%w]", 0)); 28 | } 29 | 30 | TEST(StatusTest, StatusFormatReplacePlaceholder) { 31 | BuildConfig config; 32 | StatusPrinter status(config); 33 | 34 | EXPECT_EQ("[%/s0/t0/r0/u0/f0]", 35 | status.FormatProgressStatus("[%%/s%s/t%t/r%r/u%u/f%f]", 0)); 36 | } 37 | -------------------------------------------------------------------------------- /src/string_piece.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_STRINGPIECE_H_ 16 | #define NINJA_STRINGPIECE_H_ 17 | 18 | #include 19 | 20 | #include 21 | 22 | /// StringPiece represents a slice of a string whose memory is managed 23 | /// externally. It is useful for reducing the number of std::strings 24 | /// we need to allocate. 25 | struct StringPiece { 26 | typedef const char* const_iterator; 27 | 28 | StringPiece() : str_(NULL), len_(0) {} 29 | 30 | /// The constructors intentionally allow for implicit conversions. 31 | StringPiece(const std::string& str) : str_(str.data()), len_(str.size()) {} 32 | StringPiece(const char* str) : str_(str), len_(strlen(str)) {} 33 | 34 | StringPiece(const char* str, size_t len) : str_(str), len_(len) {} 35 | 36 | bool operator==(const StringPiece& other) const { 37 | return len_ == other.len_ && memcmp(str_, other.str_, len_) == 0; 38 | } 39 | 40 | bool operator!=(const StringPiece& other) const { 41 | return !(*this == other); 42 | } 43 | 44 | /// Convert the slice into a full-fledged std::string, copying the 45 | /// data into a new string. 46 | std::string AsString() const { 47 | return len_ ? std::string(str_, len_) : std::string(); 48 | } 49 | 50 | const_iterator begin() const { 51 | return str_; 52 | } 53 | 54 | const_iterator end() const { 55 | return str_ + len_; 56 | } 57 | 58 | char operator[](size_t pos) const { 59 | return str_[pos]; 60 | } 61 | 62 | size_t size() const { 63 | return len_; 64 | } 65 | 66 | size_t empty() const { 67 | return len_ == 0; 68 | } 69 | 70 | const char* str_; 71 | size_t len_; 72 | }; 73 | 74 | #endif // NINJA_STRINGPIECE_H_ 75 | -------------------------------------------------------------------------------- /src/string_piece_util.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "string_piece_util.h" 16 | 17 | #include 18 | #include 19 | #include 20 | using namespace std; 21 | 22 | vector SplitStringPiece(StringPiece input, char sep) { 23 | vector elems; 24 | elems.reserve(count(input.begin(), input.end(), sep) + 1); 25 | 26 | StringPiece::const_iterator pos = input.begin(); 27 | 28 | for (;;) { 29 | const char* next_pos = find(pos, input.end(), sep); 30 | if (next_pos == input.end()) { 31 | elems.push_back(StringPiece(pos, input.end() - pos)); 32 | break; 33 | } 34 | elems.push_back(StringPiece(pos, next_pos - pos)); 35 | pos = next_pos + 1; 36 | } 37 | 38 | return elems; 39 | } 40 | 41 | string JoinStringPiece(const vector& list, char sep) { 42 | if (list.empty()) { 43 | return ""; 44 | } 45 | 46 | string ret; 47 | 48 | { 49 | size_t cap = list.size() - 1; 50 | for (size_t i = 0; i < list.size(); ++i) { 51 | cap += list[i].len_; 52 | } 53 | ret.reserve(cap); 54 | } 55 | 56 | for (size_t i = 0; i < list.size(); ++i) { 57 | if (i != 0) { 58 | ret += sep; 59 | } 60 | ret.append(list[i].str_, list[i].len_); 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) { 67 | if (a.len_ != b.len_) { 68 | return false; 69 | } 70 | 71 | for (size_t i = 0; i < a.len_; ++i) { 72 | if (ToLowerASCII(a.str_[i]) != ToLowerASCII(b.str_[i])) { 73 | return false; 74 | } 75 | } 76 | 77 | return true; 78 | } 79 | -------------------------------------------------------------------------------- /src/string_piece_util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_STRINGPIECE_UTIL_H_ 16 | #define NINJA_STRINGPIECE_UTIL_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "string_piece.h" 22 | 23 | std::vector SplitStringPiece(StringPiece input, char sep); 24 | 25 | std::string JoinStringPiece(const std::vector& list, char sep); 26 | 27 | inline char ToLowerASCII(char c) { 28 | return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; 29 | } 30 | 31 | bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b); 32 | 33 | #endif // NINJA_STRINGPIECE_UTIL_H_ 34 | -------------------------------------------------------------------------------- /src/string_piece_util_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "string_piece_util.h" 16 | 17 | #include "test.h" 18 | 19 | using namespace std; 20 | 21 | TEST(StringPieceUtilTest, SplitStringPiece) { 22 | { 23 | string input("a:b:c"); 24 | vector list = SplitStringPiece(input, ':'); 25 | 26 | EXPECT_EQ(list.size(), size_t(3)); 27 | 28 | EXPECT_EQ(list[0], "a"); 29 | EXPECT_EQ(list[1], "b"); 30 | EXPECT_EQ(list[2], "c"); 31 | } 32 | 33 | { 34 | string empty; 35 | vector list = SplitStringPiece(empty, ':'); 36 | 37 | EXPECT_EQ(list.size(), size_t(1)); 38 | 39 | EXPECT_EQ(list[0], ""); 40 | } 41 | 42 | { 43 | string one("a"); 44 | vector list = SplitStringPiece(one, ':'); 45 | 46 | EXPECT_EQ(list.size(), size_t(1)); 47 | 48 | EXPECT_EQ(list[0], "a"); 49 | } 50 | 51 | { 52 | string sep_only(":"); 53 | vector list = SplitStringPiece(sep_only, ':'); 54 | 55 | EXPECT_EQ(list.size(), size_t(2)); 56 | 57 | EXPECT_EQ(list[0], ""); 58 | EXPECT_EQ(list[1], ""); 59 | } 60 | 61 | { 62 | string sep(":a:b:c:"); 63 | vector list = SplitStringPiece(sep, ':'); 64 | 65 | EXPECT_EQ(list.size(), size_t(5)); 66 | 67 | EXPECT_EQ(list[0], ""); 68 | EXPECT_EQ(list[1], "a"); 69 | EXPECT_EQ(list[2], "b"); 70 | EXPECT_EQ(list[3], "c"); 71 | EXPECT_EQ(list[4], ""); 72 | } 73 | } 74 | 75 | TEST(StringPieceUtilTest, JoinStringPiece) { 76 | { 77 | string input("a:b:c"); 78 | vector list = SplitStringPiece(input, ':'); 79 | 80 | EXPECT_EQ("a:b:c", JoinStringPiece(list, ':')); 81 | EXPECT_EQ("a/b/c", JoinStringPiece(list, '/')); 82 | } 83 | 84 | { 85 | string empty; 86 | vector list = SplitStringPiece(empty, ':'); 87 | 88 | EXPECT_EQ("", JoinStringPiece(list, ':')); 89 | } 90 | 91 | { 92 | vector empty_list; 93 | 94 | EXPECT_EQ("", JoinStringPiece(empty_list, ':')); 95 | } 96 | 97 | { 98 | string one("a"); 99 | vector single_list = SplitStringPiece(one, ':'); 100 | 101 | EXPECT_EQ("a", JoinStringPiece(single_list, ':')); 102 | } 103 | 104 | { 105 | string sep(":a:b:c:"); 106 | vector list = SplitStringPiece(sep, ':'); 107 | 108 | EXPECT_EQ(":a:b:c:", JoinStringPiece(list, ':')); 109 | } 110 | } 111 | 112 | TEST(StringPieceUtilTest, ToLowerASCII) { 113 | EXPECT_EQ('a', ToLowerASCII('A')); 114 | EXPECT_EQ('z', ToLowerASCII('Z')); 115 | EXPECT_EQ('a', ToLowerASCII('a')); 116 | EXPECT_EQ('z', ToLowerASCII('z')); 117 | EXPECT_EQ('/', ToLowerASCII('/')); 118 | EXPECT_EQ('1', ToLowerASCII('1')); 119 | } 120 | 121 | TEST(StringPieceUtilTest, EqualsCaseInsensitiveASCII) { 122 | EXPECT_TRUE(EqualsCaseInsensitiveASCII("abc", "abc")); 123 | EXPECT_TRUE(EqualsCaseInsensitiveASCII("abc", "ABC")); 124 | EXPECT_TRUE(EqualsCaseInsensitiveASCII("abc", "aBc")); 125 | EXPECT_TRUE(EqualsCaseInsensitiveASCII("AbC", "aBc")); 126 | EXPECT_TRUE(EqualsCaseInsensitiveASCII("", "")); 127 | 128 | EXPECT_FALSE(EqualsCaseInsensitiveASCII("a", "ac")); 129 | EXPECT_FALSE(EqualsCaseInsensitiveASCII("/", "\\")); 130 | EXPECT_FALSE(EqualsCaseInsensitiveASCII("1", "10")); 131 | } 132 | -------------------------------------------------------------------------------- /src/test.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_TEST_H_ 16 | #define NINJA_TEST_H_ 17 | 18 | #include 19 | 20 | #include "disk_interface.h" 21 | #include "manifest_parser.h" 22 | #include "state.h" 23 | 24 | // Support utilities for tests. 25 | 26 | struct Node; 27 | 28 | /// A base test fixture that includes a State object with a 29 | /// builtin "cat" rule. 30 | struct StateTestWithBuiltinRules : public testing::Test { 31 | StateTestWithBuiltinRules(); 32 | 33 | /// Add a "cat" rule to \a state. Used by some tests; it's 34 | /// otherwise done by the ctor to state_. 35 | void AddCatRule(State* state); 36 | 37 | /// Short way to get a Node by its path from state_. 38 | Node* GetNode(const std::string& path); 39 | 40 | State state_; 41 | }; 42 | 43 | void AssertParse(State* state, const char* input, 44 | ManifestParserOptions = ManifestParserOptions()); 45 | void AssertHash(const char* expected, uint64_t actual); 46 | void VerifyGraph(const State& state); 47 | 48 | /// An implementation of DiskInterface that uses an in-memory representation 49 | /// of disk state. It also logs file accesses and directory creations 50 | /// so it can be used by tests to verify disk access patterns. 51 | struct VirtualFileSystem : public DiskInterface { 52 | VirtualFileSystem() : now_(1) {} 53 | 54 | /// "Create" a file with contents. 55 | void Create(const std::string& path, const std::string& contents); 56 | 57 | /// Tick "time" forwards; subsequent file operations will be newer than 58 | /// previous ones. 59 | int Tick() { 60 | return ++now_; 61 | } 62 | 63 | // DiskInterface 64 | virtual TimeStamp Stat(const std::string& path, std::string* err) const; 65 | virtual bool WriteFile(const std::string& path, const std::string& contents); 66 | virtual bool MakeDir(const std::string& path); 67 | virtual Status ReadFile(const std::string& path, std::string* contents, 68 | std::string* err); 69 | virtual int RemoveFile(const std::string& path); 70 | 71 | /// An entry for a single in-memory file. 72 | struct Entry { 73 | int mtime; 74 | std::string stat_error; // If mtime is -1. 75 | std::string contents; 76 | }; 77 | 78 | std::vector directories_made_; 79 | std::vector files_read_; 80 | typedef std::map FileMap; 81 | FileMap files_; 82 | std::set files_removed_; 83 | std::set files_created_; 84 | 85 | /// A simple fake timestamp for file operations. 86 | int now_; 87 | }; 88 | 89 | struct ScopedTempDir { 90 | /// Create a temporary directory and chdir into it. 91 | void CreateAndEnter(const std::string& name); 92 | 93 | /// Clean up the temporary directory. 94 | void Cleanup(); 95 | 96 | /// The temp directory containing our dir. 97 | std::string start_dir_; 98 | /// The subdirectory name for our dir, or empty if it hasn't been set up. 99 | std::string temp_dir_name_; 100 | }; 101 | 102 | /// A class that records a file path and ensures that it is removed 103 | /// on destruction. This ensures that tests do not keep stale files in the 104 | /// current directory where they run, even in case of assertion failure. 105 | struct ScopedFilePath { 106 | /// Constructor just records the file path. 107 | ScopedFilePath(const std::string& path) : path_(path) {} 108 | ScopedFilePath(const char* path) : path_(path) {} 109 | 110 | /// Allow move operations. 111 | ScopedFilePath(ScopedFilePath&&) noexcept; 112 | ScopedFilePath& operator=(ScopedFilePath&&) noexcept; 113 | 114 | /// Destructor destroys the file, unless Release() was called. 115 | ~ScopedFilePath(); 116 | 117 | /// Release the file, the destructor will not remove the file. 118 | void Release(); 119 | 120 | const char* c_str() const { return path_.c_str(); } 121 | const std::string& path() const { return path_; } 122 | bool released() const { return released_; } 123 | 124 | private: 125 | std::string path_; 126 | bool released_ = false; 127 | }; 128 | 129 | #endif // NINJA_TEST_H_ 130 | -------------------------------------------------------------------------------- /src/third_party/emhash/README.ninja: -------------------------------------------------------------------------------- 1 | Description: emhash8::HashMap for C++14/17 2 | Version: 1.6.5 (commit bdebddbdce1b473bbc189178fd523ef4a876ea01) 3 | URL: https://github.com/ktprime/emhash 4 | Copyright: Copyright (c) 2021-2024 Huang Yuanbing & bailuzhou AT 163.com 5 | SPDX-License-Identifier: MIT 6 | Local changes: 7 | - Added includes for _mm_prefetch on MinGW. 8 | - Fixed some spelling errors to appease the linter. 9 | -------------------------------------------------------------------------------- /src/third_party/rapidhash/README.ninja: -------------------------------------------------------------------------------- 1 | Description: Very fast, high quality, platform-independent hashing algorithm. 2 | Version: commit 4a6b2570e868536be84800353efd92c699f37d2c 3 | URL: https://github.com/Nicoshev/rapidhash 4 | Copyright: Copyright (C) 2024 Nicolas De Carli, Based on 'wyhash', by Wang Yi 5 | SPDX-License-Identifier: BSD-2-Clause 6 | Local changes: 7 | - Changed to UNIX line endings 8 | -------------------------------------------------------------------------------- /src/timestamp.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_TIMESTAMP_H_ 16 | #define NINJA_TIMESTAMP_H_ 17 | 18 | #ifdef _WIN32 19 | #include "win32port.h" 20 | #else 21 | #ifndef __STDC_FORMAT_MACROS 22 | #define __STDC_FORMAT_MACROS 23 | #endif 24 | #include 25 | #endif 26 | 27 | // When considering file modification times we only care to compare 28 | // them against one another -- we never convert them to an absolute 29 | // real time. On POSIX we use timespec (seconds&nanoseconds since epoch) 30 | // and on Windows we use a different value. Both fit in an int64. 31 | typedef int64_t TimeStamp; 32 | 33 | #endif // NINJA_TIMESTAMP_H_ 34 | -------------------------------------------------------------------------------- /src/version.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "version.h" 16 | 17 | #include 18 | 19 | #include "util.h" 20 | 21 | using namespace std; 22 | 23 | const char* kNinjaVersion = "1.13.0.git"; 24 | 25 | void ParseVersion(const string& version, int* major, int* minor) { 26 | size_t end = version.find('.'); 27 | *major = atoi(version.substr(0, end).c_str()); 28 | *minor = 0; 29 | if (end != string::npos) { 30 | size_t start = end + 1; 31 | end = version.find('.', start); 32 | *minor = atoi(version.substr(start, end).c_str()); 33 | } 34 | } 35 | 36 | void CheckNinjaVersion(const string& version) { 37 | int bin_major, bin_minor; 38 | ParseVersion(kNinjaVersion, &bin_major, &bin_minor); 39 | int file_major, file_minor; 40 | ParseVersion(version, &file_major, &file_minor); 41 | 42 | if (bin_major > file_major) { 43 | Warning("ninja executable version (%s) greater than build file " 44 | "ninja_required_version (%s); versions may be incompatible.", 45 | kNinjaVersion, version.c_str()); 46 | return; 47 | } 48 | 49 | if ((bin_major == file_major && bin_minor < file_minor) || 50 | bin_major < file_major) { 51 | Fatal("ninja version (%s) incompatible with build file " 52 | "ninja_required_version version (%s).", 53 | kNinjaVersion, version.c_str()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_VERSION_H_ 16 | #define NINJA_VERSION_H_ 17 | 18 | #include 19 | 20 | /// The version number of the current Ninja release. This will always 21 | /// be "git" on trunk. 22 | extern const char* kNinjaVersion; 23 | 24 | /// Parse the major/minor components of a version string. 25 | void ParseVersion(const std::string& version, int* major, int* minor); 26 | 27 | /// Check whether \a version is compatible with the current Ninja version, 28 | /// aborting if not. 29 | void CheckNinjaVersion(const std::string& required_version); 30 | 31 | #endif // NINJA_VERSION_H_ 32 | -------------------------------------------------------------------------------- /src/win32port.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef NINJA_WIN32PORT_H_ 16 | #define NINJA_WIN32PORT_H_ 17 | 18 | #if defined(__MINGW32__) || defined(__MINGW64__) 19 | #ifndef __STDC_FORMAT_MACROS 20 | #define __STDC_FORMAT_MACROS 21 | #endif 22 | #include 23 | #endif 24 | 25 | typedef signed short int16_t; 26 | typedef unsigned short uint16_t; 27 | /// A 64-bit integer type 28 | typedef signed long long int64_t; 29 | typedef unsigned long long uint64_t; 30 | 31 | // printf format specifier for uint64_t, from C99. 32 | #ifndef PRIu64 33 | #define PRId64 "I64d" 34 | #define PRIu64 "I64u" 35 | #define PRIx64 "I64x" 36 | #endif 37 | 38 | #endif // NINJA_WIN32PORT_H_ 39 | 40 | -------------------------------------------------------------------------------- /windows/ninja.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UTF-8 6 | true 7 | 8 | 9 | 10 | --------------------------------------------------------------------------------