[
  {
    "path": ".clang-format",
    "content": "# Copyright 2014 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This isn't meant to be authoritative, but it's good enough to be useful.\n# Still use your best judgement for formatting decisions: clang-format\n# sometimes makes strange choices.\n\nBasedOnStyle: Google\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nConstructorInitializerAllOnOneLineOrOnePerLine: false\nCpp11BracedListStyle: false\nIndentCaseLabels: false\nDerivePointerBinding: false\n"
  },
  {
    "path": ".clang-tidy",
    "content": "---\nChecks: '\n  ,readability-avoid-const-params-in-decls,\n  ,readability-inconsistent-declaration-parameter-name,\n  ,readability-non-const-parameter,\n  ,readability-redundant-string-cstr,\n  ,readability-redundant-string-init,\n  ,readability-simplify-boolean-expr,\n  ,cppcoreguidelines-pro-type-cstyle-cast,\n'\nWarningsAsErrors: '\n  ,readability-avoid-const-params-in-decls,\n  ,readability-inconsistent-declaration-parameter-name,\n  ,readability-non-const-parameter,\n  ,readability-redundant-string-cstr,\n  ,readability-redundant-string-init,\n  ,readability-simplify-boolean-expr,\n  ,cppcoreguidelines-pro-type-cstyle-cast,\n'\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\nend_of_line = lf\n\n[CMakeLists.txt]\nindent_style = tab\n\n[*.py]\nindent_size = 4\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  # Maintain dependencies for GitHub Actions\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/linux-musl.yml",
    "content": "name: ci-linux-musl\n\non:\n  workflow_dispatch:\n  pull_request:\n  push:\n    branches: ['**']\n    tags-ignore: ['**']  # Don't trigger on tag pushes\n  release:\n    types: [published]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\npermissions: {}\n\njobs:\n  build:\n    runs-on: ubuntu-24.04\n    container: alpine:edge\n    permissions:\n      contents: read\n    strategy:\n      fail-fast: false\n      matrix:\n        build_method: [\"python\", \"cmake\"]\n\n    steps:\n      - name: Host - checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n          persist-credentials: false\n\n      - name: Install ninja build optional dependencies\n        run: apk update && apk add -u --no-cache python3 build-base cmake re2c\n\n      - name: Configure ninja build\n        if: matrix.build_method == 'cmake'\n        run: cmake -B build -D CMAKE_BUILD_TYPE=\"Release\" -D CMAKE_COMPILE_WARNING_AS_ERROR=\"ON\"\n\n      - name: Cmake Build ninja\n        if: matrix.build_method == 'cmake'\n        run: cmake --build build --parallel --config Release\n\n      - name: Cmake test ninja\n        if: matrix.build_method == 'cmake'\n        run: build/ninja_test --gtest_color=yes\n\n      - name: Python Build ninja\n        if: matrix.build_method == 'python'\n        run: python3 configure.py --warnings-as-errors --bootstrap --verbose\n\n      - name: Python test ninja\n        if: matrix.build_method == 'python'\n        run: |\n          ./ninja all\n          python3 misc/ninja_syntax_test.py\n          # python3 misc/output_test.py\n\n      - name: Move ninja binary\n        if: matrix.build_method == 'cmake'\n        run: mv -f build/ninja ninja\n\n      - name: ninja-ninja --version\n        run: ./ninja --version >> $GITHUB_STEP_SUMMARY\n\n      - name: binary info via file\n        run: file ./ninja >> $GITHUB_STEP_SUMMARY\n"
  },
  {
    "path": ".github/workflows/linux.yml",
    "content": "name: Linux\n\non:\n  pull_request:\n  push:\n    branches: ['**']\n    tags-ignore: ['**']  # Don't trigger on tag pushes\n  release:\n    types: [published]\n\njobs:\n  fedora:\n    runs-on: [ubuntu-latest]\n    container:\n      image: fedora:40\n    steps:\n      - uses: actions/checkout@v6\n      - name: Install dependencies\n        run: dnf install -y ninja-build cmake gtest-devel re2c clang util-linux clang-tools-extra\n      - name: Linting\n        run: misc/ci.py\n      - name: Configure with CMake\n        run: cmake -Bbuild -G\"Ninja Multi-Config\" -DNINJA_CLANG_TIDY=1\n      - name: Build debug ninja\n        run: CLICOLOR_FORCE=1 ninja\n        working-directory: build\n      - name: Test debug ninja\n        working-directory: build/Debug\n        run: |\n          ./ninja_test --gtest_color=yes\n          ../../misc/output_test.py\n          ../../misc/jobserver_test.py\n      - name: Build release ninja\n        run: CLICOLOR_FORCE=1 ninja -f build-Release.ninja\n        working-directory: build\n      - name: Test release ninja\n        working-directory: build/Release\n        run: |\n          ./ninja_test --gtest_color=yes\n          ../../misc/output_test.py\n          ../../misc/jobserver_test.py\n\n  build:\n    strategy:\n      matrix:\n        host-os: [\"ubuntu-latest\", \"ubuntu-24.04-arm\"]\n        include:\n          - host-os: \"ubuntu-24.04-arm\"\n            arch: \"-aarch64\"\n      fail-fast: false\n    defaults:\n      run:\n        shell: bash\n    runs-on: ${{ matrix.host-os }}\n    container: rockylinux/rockylinux:8\n    steps:\n      - uses: actions/checkout@v6\n      - uses: codespell-project/actions-codespell@master\n        with:\n          ignore_words_list: fo,wee,addin,notin\n      - name: Install dependencies\n        run: |\n          dnf install -y make gcc-c++ libasan clang-analyzer cmake dnf-plugins-core epel-release\n          dnf config-manager --set-enabled powertools\n          dnf install -y gtest-devel p7zip p7zip-plugins ninja-build\n\n      - name: Build debug ninja\n        env:\n          CFLAGS: -fstack-protector-all -fsanitize=address\n          CXXFLAGS: -fstack-protector-all -fsanitize=address\n        run: |\n          scan-build -o scanlogs cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -B debug-build\n          scan-build -o scanlogs cmake --build debug-build --parallel --config Debug\n\n      - name: Test debug ninja\n        run: ASAN_OPTIONS=detect_leaks=0 ./ninja_test\n        working-directory: debug-build\n\n      - name: Build release ninja\n        run: |\n          cmake -GNinja -DCMAKE_BUILD_TYPE=Release -B release-build -DCMAKE_COMPILE_WARNING_AS_ERROR=1\n          cmake --build release-build --parallel --config Release\n          strip release-build/ninja\n\n      - name: Test release ninja\n        run: ./ninja_test\n        working-directory: release-build\n\n      - name: Create ninja archive\n        run: |\n          mkdir artifact\n          7z a artifact/ninja-linux${{ matrix.arch }}.zip ./release-build/ninja\n\n      # Upload ninja binary archive as an artifact\n      - name: Upload artifact\n        uses: actions/upload-artifact@v6\n        with:\n          name: ninja${{ matrix.arch }}-binary-archives\n          path: artifact\n\n      - name: Upload release asset\n        if: github.event.action == 'published'\n        uses: ncipollo/release-action@v1\n        with:\n          allowUpdates: true # if release exists it will edit it.\n          artifactContentType: \"application/zip\" # if empty defaults to raw\n          replacesArtifacts: true # will update existing release assets if needed.\n          omitBodyDuringUpdate: true # don't edit release body when published via webui\n          artifacts: ./artifact/ninja-linux${{ matrix.arch }}.zip # release asset\n\n  test:\n    permissions: # https://docs.zizmor.sh/audits/#excessive-permissions\n      contents: read\n    strategy:\n      matrix:\n        host-os: [\"ubuntu-latest\", \"ubuntu-24.04-arm\"]\n        image: [\"ubuntu:22.04\", \"ubuntu:24.04\"]\n      fail-fast: false\n    defaults:\n      run:\n        shell: bash\n    runs-on: ${{ matrix.host-os }}\n    container: ${{ matrix.image }}\n    steps:\n      - uses: actions/checkout@v6\n      - name: Install dependencies\n        run: |\n          apt update\n          apt install -y cmake python3-pytest ninja-build python3-pip clang libgtest-dev\n\n      - name: Configure (GCC)\n        run: cmake -Bbuild-gcc -DCMAKE_BUILD_TYPE=Debug -G'Ninja Multi-Config'\n\n      - name: Build (GCC, Debug)\n        run: cmake --build build-gcc --config Debug\n      - name: Unit tests (GCC, Debug)\n        run: ./build-gcc/Debug/ninja_test\n      - name: Python tests (GCC, Debug)\n        run: pytest-3 --color=yes ../..\n        working-directory: build-gcc/Debug\n\n      - name: Build (GCC, Release)\n        run: cmake --build build-gcc --config Release\n      - name: Unit tests (GCC, Release)\n        run: ./build-gcc/Release/ninja_test\n      - name: Python tests (GCC, Release)\n        run: pytest-3 --color=yes ../..\n        working-directory: build-gcc/Release\n\n      - name: Configure (Clang)\n        run: CC=clang CXX=clang++ cmake -Bbuild-clang -DCMAKE_BUILD_TYPE=Debug -G'Ninja Multi-Config'\n\n      - name: Build (Clang, Debug)\n        run: cmake --build build-clang --config Debug\n      - name: Unit tests (Clang, Debug)\n        run: ./build-clang/Debug/ninja_test\n      - name: Python tests (Clang, Debug)\n        run: pytest-3 --color=yes ../..\n        working-directory: build-clang/Debug\n\n      - name: Build (Clang, Release)\n        run: cmake --build build-clang --config Release\n      - name: Unit tests (Clang, Release)\n        run: ./build-clang/Release/ninja_test\n      - name: Python tests (Clang, Release)\n        run: pytest-3 --color=yes ../..\n        working-directory: build-clang/Release\n\n  build-with-python:\n    permissions: # https://docs.zizmor.sh/audits/#excessive-permissions\n      contents: read\n    strategy:\n      matrix:\n        host-os: [\"ubuntu-latest\", \"ubuntu-24.04-arm\"]\n        image: [\"ubuntu:22.04\", \"ubuntu:24.04\"]\n      fail-fast: false\n    defaults:\n      run:\n        shell: bash\n    runs-on: ${{ matrix.host-os }}\n    container: ${{ matrix.image }}\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          persist-credentials: false #https://docs.zizmor.sh/audits/#artipacked\n      - name: Install dependencies\n        run: |\n          apt update\n          apt install -y g++ python3\n      - name: ${{ matrix.host-os }} ${{ matrix.image }}\n        run: |\n          # Do not set --warnings-as-errors here as that triggers an irrelevant\n          # compiler warnings in <stdio.h> with ubuntu:24.04. See issue #2615\n          python3 configure.py --bootstrap\n          ./ninja all\n          python3 misc/ninja_syntax_test.py\n          ./misc/output_test.py\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: macOS\n\non:\n  pull_request:\n  push:\n    branches: ['**']\n    tags-ignore: ['**']  # Don't trigger on tag pushes\n  release:\n    types: [published]\n\njobs:\n  build:\n    runs-on: macos-14\n\n    steps:\n    - uses: actions/checkout@v6\n\n    - name: Install dependencies\n      run: brew install re2c\n\n    - name: Build ninja\n      shell: bash\n      env:\n        MACOSX_DEPLOYMENT_TARGET: 10.15\n      run: |\n        cmake -Bbuild -GXcode '-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64' -DCMAKE_COMPILE_WARNING_AS_ERROR=1\n        cmake --build build --config Release\n\n    - name: Test ninja (Release)\n      run: ./ninja_test\n      working-directory: build/Release\n\n    - name: Create ninja archive\n      shell: bash\n      run: |\n        mkdir artifact\n        7z a artifact/ninja-mac.zip ./build/Release/ninja\n\n    # Upload ninja binary archive as an artifact\n    - name: Upload artifact\n      uses: actions/upload-artifact@v6\n      with:\n        name: ninja-binary-archives\n        path: artifact\n\n    - name: Upload release asset\n      if: github.event.action == 'published'\n      uses: ncipollo/release-action@v1\n      with:\n        allowUpdates: true # if release exists it will edit it.\n        artifactContentType: \"application/zip\" # if empty defaults to raw\n        replacesArtifacts: true # will update existing release assets if needed.\n        omitBodyDuringUpdate: true # don't edit release body when published via webui\n        artifacts: ./artifact/ninja-mac.zip # release asset\n"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "name: Windows\n\non:\n  pull_request:\n  push:\n    branches: ['**']\n    tags-ignore: ['**']  # Don't trigger on tag pushes\n  release:\n    types: [published]\n\njobs:\n  build:\n    runs-on: windows-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n        - arch: 'x64'\n          suffix: ''\n        - arch: 'arm64'\n          suffix: 'arm64'\n\n    steps:\n    - uses: actions/checkout@v6\n\n    - name: Install dependencies\n      run: choco install re2c\n\n    - name: Build ninja\n      shell: bash\n      run: |\n        cmake -Bbuild -A ${{ matrix.arch }} -DCMAKE_COMPILE_WARNING_AS_ERROR=1\n        cmake --build build --parallel --config Debug\n        cmake --build build --parallel --config Release\n\n    - name: Test ninja (Debug)\n      if: matrix.arch != 'arm64'\n      run: .\\ninja_test.exe\n      working-directory: build/Debug\n\n    - name: Test ninja (Release)\n      if: matrix.arch != 'arm64'\n      run: .\\ninja_test.exe\n      working-directory: build/Release\n\n    - name: Create ninja archive\n      shell: bash\n      run: |\n        mkdir artifact\n        7z a artifact/ninja-win${{ matrix.suffix }}.zip ./build/Release/ninja.exe\n\n    # Upload ninja binary archive as an artifact\n    - name: Upload artifact\n      uses: actions/upload-artifact@v6\n      with:\n        name: ninja-binary-archives${{ matrix.suffix }}\n        path: artifact\n\n    - name: Upload release asset\n      if: github.event.action == 'published'\n      uses: ncipollo/release-action@v1\n      with:\n        allowUpdates: true # if release exists it will edit it.\n        artifactContentType: \"application/zip\" # if empty defaults to raw\n        replacesArtifacts: true # will update existing release assets if needed.\n        omitBodyDuringUpdate: true # don't edit release body when published via webui\n        artifacts: ./artifact/ninja-win${{ matrix.suffix }}.zip # release asset\n"
  },
  {
    "path": ".gitignore",
    "content": "*.pyc\n*.obj\n*.exe\n*.pdb\n*.ilk\n/build*/\n/build.ninja\n/ninja\n/ninja.bootstrap\n/build_log_perftest\n/canon_perftest\n/clparser_perftest\n/elide_middle_perftest\n/depfile_parser_perftest\n/hash_collision_bench\n/ninja_test\n/manifest_parser_perftest\n/graph.png\n/doc/manual.html\n/doc/doxygen\n*.patch\n.DS_Store\n\n# Eclipse project files\n.project\n.cproject\n\n# SublimeText project files\n*.sublime-project\n*.sublime-workspace\n\n# Ninja output\n.ninja_deps\n.ninja_log\n\n# Visual Studio Code project files\n/.vscode/\n/.ccls-cache/\n\n# Qt Creator project files\n/CMakeLists.txt.user\n\n# clangd\n/.clangd/\n/compile_commands.json\n/.cache/\n\n# Visual Studio files\n/.vs/\n/out/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\ninclude(CheckSymbolExists)\ninclude(CheckIPOSupported)\n\noption(NINJA_BUILD_BINARY \"Build ninja binary\" ON)\noption(NINJA_FORCE_PSELECT \"Use pselect() even on platforms that provide ppoll()\" OFF)\n\nproject(ninja CXX)\n\n# --- optional link-time optimization\ncheck_ipo_supported(RESULT lto_supported OUTPUT error)\n\nif(lto_supported)\n\tmessage(STATUS \"IPO / LTO enabled\")\n\tset(CMAKE_POLICY_DEFAULT_CMP0069 NEW)\n\tset(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)\nelse()\n\tmessage(STATUS \"IPO / LTO not supported: <${error}>\")\nendif()\n\n# --- compiler flags\nif(MSVC)\n\tset(CMAKE_MSVC_RUNTIME_LIBRARY \"MultiThreaded$<$<CONFIG:Debug>:Debug>\")\n\tstring(REPLACE \"/GR\" \"\" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})\n\t# Note that these settings are separately specified in configure.py, and\n\t# these lists should be kept in sync.\n\tadd_compile_options(/W4 /wd4100 /wd4267 /wd4706 /wd4702 /wd4244 /GR- /Zc:__cplusplus)\n\tadd_compile_definitions(_CRT_SECURE_NO_WARNINGS)\nelse()\n\tinclude(CheckCXXCompilerFlag)\n\tcheck_cxx_compiler_flag(-Wno-deprecated flag_no_deprecated)\n\tif(flag_no_deprecated)\n\t\tadd_compile_options(-Wno-deprecated)\n\tendif()\n\tif(CMAKE_VERSION VERSION_LESS 3.24)\n\t\tcheck_cxx_compiler_flag(-fdiagnostics-color flag_color_diag)\n\t\tif(flag_color_diag)\n\t\t\tadd_compile_options(-fdiagnostics-color)\n\t\tendif()\n\telseif(NOT DEFINED ENV{CMAKE_COLOR_DIAGNOSTICS})\n\t\tset(CMAKE_COLOR_DIAGNOSTICS ON)\n\tendif()\n\n\tif(NOT NINJA_FORCE_PSELECT)\n\t\t# Check whether ppoll() is usable on the target platform.\n\t\t# Set -DUSE_PPOLL=1 if this is the case.\n\t\t#\n\t\t# NOTE: Use check_cxx_symbol_exists() instead of check_symbol_exists()\n\t\t# because on Linux, <poll.h> only exposes the symbol when _GNU_SOURCE\n\t\t# is defined.\n\t\t#\n\t\t# Both g++ and clang++ define the symbol by default, because the C++\n\t\t# standard library headers require it, but *not* gcc and clang, which\n\t\t# are used by check_symbol_exists().\n\t\tinclude(CheckCXXSymbolExists)\n\t\tcheck_cxx_symbol_exists(ppoll poll.h HAVE_PPOLL)\n\t\tif(HAVE_PPOLL)\n\t\t\tadd_compile_definitions(USE_PPOLL=1)\n\t\tendif()\n\tendif()\nendif()\n\n# --- optional re2c\nset(RE2C_MAJOR_VERSION 0)\nfind_program(RE2C re2c)\nif(RE2C)\n\texecute_process(COMMAND \"${RE2C}\" --vernum OUTPUT_VARIABLE RE2C_RAW_VERSION)\n\tmath(EXPR RE2C_MAJOR_VERSION \"${RE2C_RAW_VERSION} / 10000\")\nendif()\nif(${RE2C_MAJOR_VERSION} GREATER 1)\n\t# the depfile parser and ninja lexers are generated using re2c.\n\tfunction(re2c IN OUT)\n\t\tadd_custom_command(DEPENDS ${IN} OUTPUT ${OUT}\n\t\t\tCOMMAND ${RE2C} -b -i --no-generation-date --no-version -o ${OUT} ${IN}\n\t\t)\n\tendfunction()\n\tre2c(${PROJECT_SOURCE_DIR}/src/depfile_parser.in.cc ${PROJECT_BINARY_DIR}/depfile_parser.cc)\n\tre2c(${PROJECT_SOURCE_DIR}/src/lexer.in.cc ${PROJECT_BINARY_DIR}/lexer.cc)\n\tadd_library(libninja-re2c OBJECT ${PROJECT_BINARY_DIR}/depfile_parser.cc ${PROJECT_BINARY_DIR}/lexer.cc)\nelse()\n\tmessage(WARNING \"re2c 2 or later was not found; changes to src/*.in.cc will not affect your build.\")\n\tadd_library(libninja-re2c OBJECT src/depfile_parser.cc src/lexer.cc)\nendif()\ntarget_include_directories(libninja-re2c PRIVATE src)\n\n# --- Check for 'browse' mode support\nfunction(check_platform_supports_browse_mode RESULT)\n\t# Make sure the inline.sh script works on this platform.\n\t# It uses the shell commands such as 'od', which may not be available.\n\n\texecute_process(\n\t\tCOMMAND sh -c \"echo 'TEST' | src/inline.sh var\"\n\t\tWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}\n\t\tRESULT_VARIABLE inline_result\n\t\tOUTPUT_QUIET\n\t\tERROR_QUIET\n\t)\n\tif(NOT inline_result EQUAL \"0\")\n\t\t# The inline script failed, so browse mode is not supported.\n\t\tset(${RESULT} \"0\" PARENT_SCOPE)\n\t\tif(NOT WIN32)\n\t\t\tmessage(WARNING \"browse feature omitted due to inline script failure\")\n\t\tendif()\n\t\treturn()\n\tendif()\n\n\t# Now check availability of the unistd header\n\tcheck_symbol_exists(fork \"unistd.h\" HAVE_FORK)\n\tcheck_symbol_exists(pipe \"unistd.h\" HAVE_PIPE)\n\tset(browse_supported 0)\n\tif (HAVE_FORK AND HAVE_PIPE)\n\t\tset(browse_supported 1)\n\tendif ()\n\tset(${RESULT} \"${browse_supported}\" PARENT_SCOPE)\n\tif(NOT browse_supported)\n\t\tmessage(WARNING \"browse feature omitted due to missing `fork` and `pipe` functions\")\n\tendif()\n\nendfunction()\n\nset(NINJA_PYTHON \"python\" CACHE STRING \"Python interpreter to use for the browse tool\")\n\ncheck_platform_supports_browse_mode(platform_supports_ninja_browse)\n\n# Core source files all build into ninja library.\nadd_library(libninja OBJECT\n\tsrc/build_log.cc\n\tsrc/build.cc\n\tsrc/clean.cc\n\tsrc/clparser.cc\n\tsrc/dyndep.cc\n\tsrc/dyndep_parser.cc\n\tsrc/debug_flags.cc\n\tsrc/deps_log.cc\n\tsrc/disk_interface.cc\n\tsrc/edit_distance.cc\n\tsrc/elide_middle.cc\n\tsrc/eval_env.cc\n\tsrc/graph.cc\n\tsrc/graphviz.cc\n\tsrc/jobserver.cc\n\tsrc/json.cc\n\tsrc/line_printer.cc\n\tsrc/manifest_parser.cc\n\tsrc/metrics.cc\n\tsrc/missing_deps.cc\n\tsrc/parser.cc\n\tsrc/real_command_runner.cc\n\tsrc/state.cc\n\tsrc/status_printer.cc\n\tsrc/string_piece_util.cc\n\tsrc/util.cc\n\tsrc/version.cc\n)\nif(WIN32)\n\ttarget_sources(libninja PRIVATE\n\t\tsrc/subprocess-win32.cc\n\t\tsrc/includes_normalize-win32.cc\n\t\tsrc/jobserver-win32.cc\n\t\tsrc/msvc_helper-win32.cc\n\t\tsrc/msvc_helper_main-win32.cc\n\t\tsrc/getopt.c\n\t\tsrc/minidump-win32.cc\n\t)\n\t# Build getopt.c, which can be compiled as either C or C++, as C++\n\t# so that build environments which lack a C compiler, but have a C++\n\t# compiler may build ninja.\n\tset_source_files_properties(src/getopt.c PROPERTIES LANGUAGE CXX)\n\n\t# windows.h defines min() and max() which conflict with std::min()\n\t# and std::max(), which both might be used in sources. Avoid compile\n\t# errors by telling windows.h to not define those two.\n\tadd_compile_definitions(NOMINMAX)\nelse()\n\ttarget_sources(libninja PRIVATE\n\t\tsrc/jobserver-posix.cc\n\t\tsrc/subprocess-posix.cc\n\t)\n\tif(CMAKE_SYSTEM_NAME STREQUAL \"OS400\" OR CMAKE_SYSTEM_NAME STREQUAL \"AIX\")\n\t\ttarget_sources(libninja PRIVATE src/getopt.c)\n\t\t# Build getopt.c, which can be compiled as either C or C++, as C++\n\t\t# so that build environments which lack a C compiler, but have a C++\n\t\t# compiler may build ninja.\n\t\tset_source_files_properties(src/getopt.c PROPERTIES LANGUAGE CXX)\n\tendif()\n\n\t# Needed for perfstat_cpu_total\n\tif(CMAKE_SYSTEM_NAME STREQUAL \"AIX\")\n\t\ttarget_link_libraries(libninja PUBLIC \"-lperfstat\")\n\tendif()\nendif()\n\ntarget_compile_features(libninja PUBLIC cxx_std_17)\ntarget_compile_features(libninja-re2c PUBLIC cxx_std_17)\n\n#Fixes GetActiveProcessorCount on MinGW\nif(MINGW)\ntarget_compile_definitions(libninja PRIVATE _WIN32_WINNT=0x0601 __USE_MINGW_ANSI_STDIO=1)\nendif()\n\n# On IBM i (identified as \"OS400\" for compatibility reasons) and AIX, this fixes missing\n# PRId64 (and others) at compile time in C++ sources\nif(CMAKE_SYSTEM_NAME STREQUAL \"OS400\" OR CMAKE_SYSTEM_NAME STREQUAL \"AIX\")\n\tadd_compile_definitions(__STDC_FORMAT_MACROS)\nendif()\n\n# Main executable is library plus main() function.\nif(NINJA_BUILD_BINARY)\n\tadd_executable(ninja src/ninja.cc)\n\ttarget_link_libraries(ninja PRIVATE libninja libninja-re2c)\n\n\tif(WIN32)\n\t\ttarget_sources(ninja PRIVATE windows/ninja.manifest)\n\tendif()\n\n\toption(NINJA_CLANG_TIDY \"Run clang-tidy on source files\" OFF)\n\tif(NINJA_CLANG_TIDY)\n\t\tset_target_properties(libninja PROPERTIES CXX_CLANG_TIDY \"clang-tidy;--use-color\")\n\t\tset_target_properties(ninja    PROPERTIES CXX_CLANG_TIDY \"clang-tidy;--use-color\")\n\tendif()\nendif()\n\n# Adds browse mode into the ninja binary if it's supported by the host platform.\nif(platform_supports_ninja_browse)\n\t# Inlines src/browse.py into the browse_py.h header, so that it can be included\n\t# by src/browse.cc\n\tadd_custom_command(\n\t\tOUTPUT build/browse_py.h\n\t\tMAIN_DEPENDENCY src/browse.py\n\t\tDEPENDS src/inline.sh\n\t\tCOMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/build\n\t\tCOMMAND src/inline.sh kBrowsePy\n\t\t\t\t\t\t< src/browse.py\n\t\t\t\t\t\t> ${PROJECT_BINARY_DIR}/build/browse_py.h\n\t\tWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}\n\t\tVERBATIM\n\t)\n\n\tif(NINJA_BUILD_BINARY)\n\t\ttarget_compile_definitions(ninja PRIVATE NINJA_HAVE_BROWSE)\n\t\ttarget_sources(ninja PRIVATE src/browse.cc)\n\tendif()\n\tset_source_files_properties(src/browse.cc\n\t\tPROPERTIES\n\t\t\tOBJECT_DEPENDS \"${PROJECT_BINARY_DIR}/build/browse_py.h\"\n\t\t\tINCLUDE_DIRECTORIES \"${PROJECT_BINARY_DIR}\"\n\t\t\tCOMPILE_DEFINITIONS NINJA_PYTHON=\"${NINJA_PYTHON}\"\n\t)\nendif()\n\ninclude(CTest)\nif(BUILD_TESTING)\n\n  # Can be removed if cmake min version is >=3.24\n  if (POLICY CMP0135)\n    cmake_policy(SET CMP0135 NEW)\n  endif()\n\n  find_package(GTest)\n  if(NOT GTest_FOUND)\n    include(FetchContent)\n    FetchContent_Declare(\n      googletest\n      # GoogleTest v1.17.0 requires at least cmake 3.16, v1.16.0 requires at least cmake 3.13 which\n\t  # is within the ninja project minimum requirement.\n      URL https://github.com/google/googletest/releases/download/v1.16.0/googletest-1.16.0.tar.gz\n      URL_HASH SHA256=78c676fc63881529bf97bf9d45948d905a66833fbfa5318ea2cd7478cb98f399\n    )\n    FetchContent_MakeAvailable(googletest)\n  endif()\n\n  # Tests all build into ninja_test executable.\n  add_executable(ninja_test\n    src/build_log_test.cc\n    src/build_test.cc\n    src/clean_test.cc\n    src/clparser_test.cc\n    src/depfile_parser_test.cc\n    src/deps_log_test.cc\n    src/disk_interface_test.cc\n    src/dyndep_parser_test.cc\n    src/edit_distance_test.cc\n    src/elide_middle_test.cc\n    src/explanations_test.cc\n    src/graph_test.cc\n    src/jobserver_test.cc\n    src/json_test.cc\n    src/lexer_test.cc\n    src/manifest_parser_test.cc\n    src/missing_deps_test.cc\n    src/ninja_test.cc\n    src/state_test.cc\n    src/string_piece_util_test.cc\n    src/subprocess_test.cc\n    src/test.cc\n    src/util_test.cc\n  )\n  if(WIN32)\n    target_sources(ninja_test PRIVATE src/includes_normalize_test.cc src/msvc_helper_test.cc\n      windows/ninja.manifest)\n\n    if(MSVC)\n      # Silence warnings about using unlink rather than _unlink\n      target_compile_definitions(ninja_test PRIVATE _CRT_NONSTDC_NO_DEPRECATE)\n    endif()\n  endif()\n  find_package(Threads REQUIRED)\n  target_link_libraries(ninja_test PRIVATE libninja libninja-re2c GTest::gtest Threads::Threads)\n\n  foreach(perftest\n    build_log_perftest\n    canon_perftest\n    clparser_perftest\n    depfile_parser_perftest\n    elide_middle_perftest\n    hash_collision_bench\n    manifest_parser_perftest\n  )\n    add_executable(${perftest} src/${perftest}.cc)\n    target_link_libraries(${perftest} PRIVATE libninja libninja-re2c)\n  endforeach()\n\n  if(CMAKE_SYSTEM_NAME STREQUAL \"AIX\" AND CMAKE_SIZEOF_VOID_P EQUAL 4)\n    # These tests require more memory than will fit in the standard AIX shared stack/heap (256M)\n    target_link_options(hash_collision_bench PRIVATE \"-Wl,-bmaxdata:0x80000000\")\n    target_link_options(manifest_parser_perftest PRIVATE \"-Wl,-bmaxdata:0x80000000\")\n  endif()\n\n  add_test(NAME NinjaTest COMMAND ninja_test)\nendif()\n\nif(NINJA_BUILD_BINARY)\n\tinstall(TARGETS ninja)\nendif()\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to successfully make changes to Ninja\n\nWe're very wary of changes that increase the complexity of Ninja (in particular,\nnew build file syntax or command-line flags) or increase the maintenance burden\nof Ninja. Ninja is already successfully used by hundreds of developers for large\nprojects and it already achieves (most of) the goals we set out for it to do.\nIt's probably best to discuss new feature ideas on the\n[mailing list](https://groups.google.com/forum/#!forum/ninja-build) or in an\nissue before creating a PR.\n\n## Coding guidelines\n\nGenerally it's the\n[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) with\na few additions:\n\n* We have used `using namespace std;` a lot in the past. For new contributions,\n  please try to avoid relying on it and instead whenever possible use `std::`.\n  However, please do not change existing code simply to add `std::` unless your\n  contribution already needs to change that line of code anyway.\n* Use `///` for [Doxygen](http://www.doxygen.nl/) (use `\\a` to refer to\n  arguments).\n* It's not necessary to document each argument, especially when they're\n  relatively self-evident (e.g. in\n  `CanonicalizePath(string* path, string* err)`, the arguments are hopefully\n  obvious).\n\nIf you're unsure about code formatting, please use\n[clang-format](https://clang.llvm.org/docs/ClangFormat.html). However, please do\nnot format code that is not otherwise part of your contribution.\n"
  },
  {
    "path": "COPYING",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2010\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Ninja\n\nNinja is a small build system with a focus on speed.\nhttps://ninja-build.org/\n\nSee [the manual](https://ninja-build.org/manual.html) or\n`doc/manual.asciidoc` included in the distribution for background\nand more details.\n\nBinaries for Linux, Mac and Windows are available on\n  [GitHub](https://github.com/ninja-build/ninja/releases).\nRun `./ninja -h` for Ninja help.\n\nInstallation is not necessary because the only required file is the\nresulting ninja binary. However, to enable features like Bash\ncompletion and Emacs and Vim editing modes, some files in misc/ must be\ncopied to appropriate locations.\n\nIf you're interested in making changes to Ninja, read\n[CONTRIBUTING.md](CONTRIBUTING.md) first.\n\n## Building Ninja itself\n\nYou can either build Ninja via the custom generator script written in Python or\nvia CMake. For more details see\n[the wiki](https://github.com/ninja-build/ninja/wiki).\n\n### Python\n\n```\n./configure.py --bootstrap\n```\n\nThis will generate the `ninja` binary and a `build.ninja` file you can now use\nto build Ninja with itself.\n\nIf you have a GoogleTest source directory, you can build the tests\nby passing its path with `--gtest-source-dir=PATH` option, or the\n`GTEST_SOURCE_DIR` environment variable, e.g.:\n\n```sh\n./configure.py --bootstrap --gtest-source-dir=/path/to/googletest\n./ninja all     # build ninja_test and other auxiliary binaries\n./ninja_test    # run the unit-test suite.\n```\n\nUse the CMake build below if you want to use a preinstalled binary\nversion of the library.\n\n### CMake\n\nTo build the ninja binary without building the unit tests, disable test building\nby setting `BUILD_TESTING` to `OFF`:\n\n```\ncmake -Bbuild-cmake -DBUILD_TESTING=OFF\ncmake --build build-cmake\n```\n\nThe `ninja` binary will now be inside the `build-cmake` directory (you can\nchoose any other name you like).\n\nTo run the unit tests, omit the `-DBUILD_TESTING=OFF` option, and after\nbuilding, run:\n\n```\nbuild-cmake/ninja_test\n```\n\n## Generating documentation\n\n### Ninja Manual\n\nYou must have `asciidoc` and `xsltproc` in your PATH, then do:\n\n```\n./configure.py\nninja manual doc/manual.html\n```\n\nWhich will generate `doc/manual.html`.\n\nTo generate the PDF version of the manual, you must have `dblatext` in your PATH\nthen do:\n\n```sh\n./configure.py    # only if you didn't do it previously.\nninja doc/manual.pdf\n```\n\nWhich will generate `doc/manual.pdf`.\n\n### Doxygen documentation\n\nIf you have `doxygen` installed, you can build documentation extracted from C++\ndeclarations and comments to help you navigate the code. Note that Ninja is a\nstandalone executable, not a library, so there is no public API, all details\nexposed here are internal.\n\n```sh\n./configure.py   # if needed\nninja doxygen\n```\n\nThen open `doc/doxygen/html/index.html` in a browser to look at it.\n"
  },
  {
    "path": "RELEASING.md",
    "content": "Notes to myself on all the steps to make for a Ninja release.\n\n### Push new release branch:\n1. Run afl-fuzz for a day or so and run ninja_test\n2. Consider sending a heads-up to the ninja-build mailing list first\n3. Make sure branches 'master' and 'release' are synced up locally\n4. Update src/version.cc with new version (with \".git\"), then\n   ```\n   git commit -am 'mark this 1.5.0.git'\n   ```\n5. git checkout release; git merge master\n6. Fix version number in src/version.cc (it will likely conflict in the above)\n7. Fix version in doc/manual.asciidoc (exists only on release branch)\n8. commit, tag, push (don't forget to push --tags)\n   ```\n   git commit -am v1.5.0; git push origin release\n   git tag v1.5.0; git push --tags\n   # Push the 1.5.0.git change on master too:\n   git checkout master; git push origin master\n   ```\n9. Construct release notes from prior notes\n\n   credits: `git shortlog -s --no-merges REV..`\n\n\n### Release on GitHub:\n1. Go to [Tags](https://github.com/ninja-build/ninja/tags)\n2. Open the newly created tag and select \"Create release from tag\"\n3. Create the release which will trigger a build which automatically attaches\n   the binaries\n\n### Make announcement on mailing list:\n1. copy old mail\n\n### Update website:\n1. Make sure your ninja checkout is on the v1.5.0 tag\n2. Clone https://github.com/ninja-build/ninja-build.github.io\n3. In that repo, `./update-docs.sh`\n4. Update index.html with newest version and link to release notes\n5. `git commit -m 'run update-docs.sh, 1.5.0 release'`\n6. `git push origin master`\n"
  },
  {
    "path": "appveyor.yml",
    "content": "version: 1.0.{build}\nimage:\n  - Visual Studio 2017\n  - Ubuntu2204\n\nenvironment:\n  CLICOLOR_FORCE: 1\n  CHERE_INVOKING: 1 # Tell Bash to inherit the current working directory\n  matrix:\n    - MSYSTEM: MINGW64\n    - MSYSTEM: LINUX\n\nmatrix:\n  exclude:\n    - image: Visual Studio 2017\n      MSYSTEM: LINUX\n    - image: Ubuntu2204\n      MSYSTEM: MINGW64\n\nfor:\n  -\n    matrix:\n      only:\n        - MSYSTEM: MINGW64\n    build_script:\n      ps: \"C:\\\\msys64\\\\usr\\\\bin\\\\bash -lc @\\\"\\n\n      pacman -S --quiet --noconfirm --needed re2c 2>&1\\n\n      ./configure.py --bootstrap --platform mingw 2>&1\\n\n      ./ninja all\\n\n      ./misc/ninja_syntax_test.py 2>&1\\n\\\"@\"\n  - matrix:\n      only:\n        - image: Ubuntu2204\n    build_script:\n      - ./configure.py --bootstrap\n      - ./ninja all\n      - misc/ninja_syntax_test.py\n      - misc/output_test.py\n\ntest: off\n"
  },
  {
    "path": "configure.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright 2001 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Script that generates the build.ninja for ninja itself.\n\nProjects that use ninja themselves should either write a similar script\nor use a meta-build system that supports Ninja output.\"\"\"\n\nfrom optparse import OptionParser\nimport os\nimport shlex\nimport subprocess\nimport sys\nfrom typing import Optional, Union, Dict, List, Any, TYPE_CHECKING\n\nsourcedir = os.path.dirname(os.path.realpath(__file__))\nsys.path.insert(0, os.path.join(sourcedir, 'misc'))\nif TYPE_CHECKING:\n    import misc.ninja_syntax as ninja_syntax\nelse:\n    import ninja_syntax\n\n\nclass Platform(object):\n    \"\"\"Represents a host/target platform and its specific build attributes.\"\"\"\n    def __init__(self, platform: Optional[str]) -> None:\n        self._platform = platform\n        if self._platform is not None:\n            return\n        self._platform = sys.platform\n        if self._platform.startswith('linux'):\n            self._platform = 'linux'\n        elif self._platform.startswith('freebsd'):\n            self._platform = 'freebsd'\n        elif self._platform.startswith('gnukfreebsd'):\n            self._platform = 'freebsd'\n        elif self._platform.startswith('openbsd'):\n            self._platform = 'openbsd'\n        elif self._platform.startswith('solaris') or self._platform == 'sunos5':\n            self._platform = 'solaris'\n        elif self._platform.startswith('mingw'):\n            self._platform = 'mingw'\n        elif self._platform.startswith('win'):\n            self._platform = 'msvc'\n        elif self._platform.startswith('bitrig'):\n            self._platform = 'bitrig'\n        elif self._platform.startswith('netbsd'):\n            self._platform = 'netbsd'\n        elif self._platform.startswith('aix'):\n            self._platform = 'aix'\n        elif self._platform.startswith('os400'):\n            self._platform = 'os400'\n        elif self._platform.startswith('dragonfly'):\n            self._platform = 'dragonfly'\n\n    @staticmethod\n    def known_platforms() -> List[str]:\n      return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5',\n              'mingw', 'msvc', 'gnukfreebsd', 'bitrig', 'netbsd', 'aix',\n              'dragonfly']\n\n    def platform(self) -> str:\n        return self._platform  # type: ignore # Incompatible return value type\n\n    def is_linux(self) -> bool:\n        return self._platform == 'linux'\n\n    def is_mingw(self) -> bool:\n        return self._platform == 'mingw'\n\n    def is_msvc(self) -> bool:\n        return self._platform == 'msvc'\n\n    def msvc_needs_fs(self) -> bool:\n        popen = subprocess.Popen(['cl', '/nologo', '/help'],\n                                 stdout=subprocess.PIPE,\n                                 stderr=subprocess.PIPE)\n        out, err = popen.communicate()\n        return b'/FS' in out\n\n    def is_windows(self) -> bool:\n        return self.is_mingw() or self.is_msvc()\n\n    def is_solaris(self) -> bool:\n        return self._platform == 'solaris'\n\n    def is_aix(self) -> bool:\n        return self._platform == 'aix'\n\n    def is_os400_pase(self) -> bool:\n        return self._platform == 'os400' or os.uname().sysname.startswith('OS400')  # type: ignore # Module has no attribute \"uname\"\n\n    def uses_usr_local(self) -> bool:\n        return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly', 'netbsd')\n\n    def supports_ppoll(self) -> bool:\n        return self._platform in ('freebsd', 'linux', 'openbsd', 'bitrig',\n                                  'dragonfly')\n\n    def supports_ninja_browse(self) -> bool:\n        return (not self.is_windows()\n                and not self.is_solaris()\n                and not self.is_aix())\n\n    def can_rebuild_in_place(self) -> bool:\n        return not (self.is_windows() or self.is_aix())\n\nclass Bootstrap:\n    \"\"\"API shim for ninja_syntax.Writer that instead runs the commands.\n\n    Used to bootstrap Ninja from scratch.  In --bootstrap mode this\n    class is used to execute all the commands to build an executable.\n    It also proxies all calls to an underlying ninja_syntax.Writer, to\n    behave like non-bootstrap mode.\n    \"\"\"\n    def __init__(self, writer: ninja_syntax.Writer, verbose: bool = False) -> None:\n        self.writer = writer\n        self.verbose = verbose\n        # Map of variable name => expanded variable value.\n        self.vars: Dict[str, str] = {}\n        # Map of rule name => dict of rule attributes.\n        self.rules: Dict[str, Dict[str, Any]] = {\n            'phony': {}\n        }\n\n    def comment(self, text: str) -> None:\n        return self.writer.comment(text)\n\n    def newline(self) -> None:\n        return self.writer.newline()\n\n    def variable(self, key: str, val: str) -> None:\n        # In bootstrap mode, we have no ninja process to catch /showIncludes\n        # output.\n        self.vars[key] = self._expand(val).replace('/showIncludes', '')\n        return self.writer.variable(key, val)\n\n    def rule(self, name: str, **kwargs: Any) -> None:\n        self.rules[name] = kwargs\n        return self.writer.rule(name, **kwargs)\n\n    def build(\n        self,\n        outputs: Union[str, List[str]],\n        rule: str,\n        inputs: Optional[Union[str, List[str]]] = None,\n        **kwargs: Any\n    ) -> List[str]:\n        ruleattr = self.rules[rule]\n        cmd = ruleattr.get('command')\n        if cmd is None:  # A phony rule, for example.\n            return  # type: ignore # Return value expected\n\n        # Implement just enough of Ninja variable expansion etc. to\n        # make the bootstrap build work.\n        local_vars = {\n            'in': self._expand_paths(inputs),\n            'out': self._expand_paths(outputs)\n        }\n        for key, val in kwargs.get('variables', []):\n            local_vars[key] = ' '.join(ninja_syntax.as_list(val))\n\n        self._run_command(self._expand(cmd, local_vars))\n\n        return self.writer.build(outputs, rule, inputs, **kwargs)\n\n    def default(self, paths: Union[str, List[str]]) -> None:\n        return self.writer.default(paths)\n\n    def _expand_paths(self, paths: Optional[Union[str, List[str]]]) -> str:\n        \"\"\"Expand $vars in an array of paths, e.g. from a 'build' block.\"\"\"\n        paths = ninja_syntax.as_list(paths)\n        return ' '.join(map(self._shell_escape, (map(self._expand, paths))))\n\n    def _expand(self, str: str, local_vars: Dict[str, str] = {}) -> str:\n        \"\"\"Expand $vars in a string.\"\"\"\n        return ninja_syntax.expand(str, self.vars, local_vars)\n\n    def _shell_escape(self, path: str) -> str:\n        \"\"\"Quote paths containing spaces.\"\"\"\n        return '\"%s\"' % path if ' ' in path else path\n\n    def _run_command(self, cmdline: str) -> None:\n        \"\"\"Run a subcommand, quietly.  Prints the full command on error.\"\"\"\n        try:\n            if self.verbose:\n                print(cmdline)\n            subprocess.check_call(cmdline, shell=True)\n        except subprocess.CalledProcessError:\n            print('when running: ', cmdline)\n            raise\n\n\nparser = OptionParser()\nprofilers = ['gmon', 'pprof']\nparser.add_option('--bootstrap', action='store_true',\n                  help='bootstrap a ninja binary from nothing')\nparser.add_option('--verbose', action='store_true',\n                  help='enable verbose build')\nparser.add_option('--platform',\n                  help='target platform (' +\n                       '/'.join(Platform.known_platforms()) + ')',\n                  choices=Platform.known_platforms())\nparser.add_option('--host',\n                  help='host platform (' +\n                       '/'.join(Platform.known_platforms()) + ')',\n                  choices=Platform.known_platforms())\nparser.add_option('--debug', action='store_true',\n                  help='enable debugging extras',)\nparser.add_option('--warnings-as-errors', action='store_true',\n                  help='convert warnings into errors during build',)\nparser.add_option('--profile', metavar='TYPE',\n                  choices=profilers,\n                  help='enable profiling (' + '/'.join(profilers) + ')',)\nparser.add_option('--gtest-source-dir', metavar='PATH',\n                  help='Path to GoogleTest source directory. If not provided ' +\n                       'GTEST_SOURCE_DIR will be probed in the environment. ' +\n                       'Tests will not be built without a value.')\nparser.add_option('--with-python', metavar='EXE',\n                  help='use EXE as the Python interpreter',\n                  default=os.path.basename(sys.executable))\nparser.add_option('--force-pselect', action='store_true',\n                  help='ppoll() is used by default where available, '\n                       'but some platforms may need to use pselect instead',)\n(options, args) = parser.parse_args()\nif args:\n    print('ERROR: extra unparsed command-line arguments:', args)\n    sys.exit(1)\n\nplatform = Platform(options.platform)\nif options.host:\n    host = Platform(options.host)\nelse:\n    host = platform\n\nBUILD_FILENAME = 'build.ninja'\nninja_writer = ninja_syntax.Writer(open(BUILD_FILENAME, 'w'))\nn: Union[ninja_syntax.Writer, Bootstrap] = ninja_writer\n\nif options.bootstrap:\n    # Make the build directory.\n    try:\n        os.mkdir('build')\n    except OSError:\n        pass\n    # Wrap ninja_writer with the Bootstrapper, which also executes the\n    # commands.\n    print('bootstrapping ninja...')\n    n = Bootstrap(n, verbose=options.verbose)  # type: ignore # Incompatible types in assignment\n\nn.comment('This file is used to build ninja itself.')\nn.comment('It is generated by ' + os.path.basename(__file__) + '.')\nn.newline()\n\nn.variable('ninja_required_version', '1.3')\nn.newline()\n\nn.comment('The arguments passed to configure.py, for rerunning it.')\nconfigure_args = sys.argv[1:]\nif '--bootstrap' in configure_args:\n    configure_args.remove('--bootstrap')\nn.variable('configure_args', ' '.join(configure_args))\nenv_keys = set(['CXX', 'AR', 'CFLAGS', 'CXXFLAGS', 'LDFLAGS'])\nconfigure_env = dict((k, os.environ[k]) for k in os.environ if k in env_keys)\nif configure_env:\n    config_str = ' '.join([k + '=' + shlex.quote(configure_env[k])\n                           for k in configure_env])\n    n.variable('configure_env', config_str + '$ ')\nn.newline()\n\nCXX = configure_env.get('CXX', 'c++')\nobjext = '.o'\nif platform.is_msvc():\n    CXX = 'cl'\n    objext = '.obj'\n\ndef src(filename: str) -> str:\n    return os.path.join('$root', 'src', filename)\ndef built(filename: str) -> str:\n    return os.path.join('$builddir', filename)\ndef doc(filename: str) -> str:\n    return os.path.join('$root', 'doc', filename)\ndef cc(name: str, **kwargs: Any) -> List[str]:\n    return n.build(built(name + objext), 'cxx', src(name + '.c'), **kwargs)\ndef cxx(name: str, **kwargs: Any) -> List[str]:\n    return n.build(built(name + objext), 'cxx', src(name + '.cc'), **kwargs)\ndef binary(name: str) -> str:\n    if platform.is_windows():\n        exe = name + '.exe'\n        n.build(name, 'phony', exe)\n        return exe\n    return name\n\nroot = sourcedir\nif root == os.getcwd():\n    # In the common case where we're building directly in the source\n    # tree, simplify all the paths to just be cwd-relative.\n    root = '.'\nn.variable('root', root)\nn.variable('builddir', 'build')\nn.variable('cxx', CXX)\nif platform.is_msvc():\n    n.variable('ar', 'link')\nelse:\n    n.variable('ar', configure_env.get('AR', 'ar'))\n\ndef search_system_path(file_name: str) -> Optional[str]:  # type: ignore # Missing return statement\n  \"\"\"Find a file in the system path.\"\"\"\n  for dir in os.environ['path'].split(';'):\n    path = os.path.join(dir, file_name)\n    if os.path.exists(path):\n      return path\n\n# Note that build settings are separately specified in CMakeLists.txt and\n# these lists should be kept in sync.\nif platform.is_msvc():\n    if not search_system_path('cl.exe'):\n        raise Exception('cl.exe not found. Run again from the Developer Command Prompt for VS')\n    cflags = ['/showIncludes',\n              '/nologo',  # Don't print startup banner.\n              '/utf-8',\n              '/std:c++17',\n              '/Zi',  # Create pdb with debug info.\n              '/W4',  # Highest warning level.\n              '/WX',  # Warnings as errors.\n              '/wd4530', '/wd4100', '/wd4706', '/wd4244',\n              '/wd4512', '/wd4800', '/wd4702',\n              # Disable warnings about constant conditional expressions.\n              '/wd4127',\n              # Disable warnings about passing \"this\" during initialization.\n              '/wd4355',\n              # Disable warnings about ignored typedef in DbgHelp.h\n              '/wd4091',\n              '/GR-',  # Disable RTTI.\n              '/Zc:__cplusplus',\n              # Disable size_t -> int truncation warning.\n              # We never have strings or arrays larger than 2**31.\n              '/wd4267',\n              '/DNOMINMAX', '/D_CRT_SECURE_NO_WARNINGS',\n              '/D_HAS_EXCEPTIONS=0',\n              '/DNINJA_PYTHON=\"%s\"' % options.with_python]\n    if options.warnings_as_errors:\n        cflags.append('/WX')\n    if platform.msvc_needs_fs():\n        cflags.append('/FS')\n    ldflags = ['/DEBUG', '/libpath:$builddir']\n    if not options.debug:\n        cflags += ['/Ox', '/DNDEBUG', '/GL']\n        ldflags += ['/LTCG', '/OPT:REF', '/OPT:ICF']\nelse:\n    cflags = ['-g', '-Wall', '-Wextra',\n              '-Wno-deprecated',\n              '-Wno-missing-field-initializers',\n              '-Wno-unused-parameter',\n              '-fno-rtti',\n              '-fno-exceptions',\n              '-std=c++17',\n              '-fvisibility=hidden', '-pipe',\n              '-DNINJA_PYTHON=\"%s\"' % options.with_python]\n    if options.warnings_as_errors:\n        cflags += [ \"-Werror\" ]\n    if options.debug:\n        cflags += ['-D_GLIBCXX_DEBUG', '-D_GLIBCXX_DEBUG_PEDANTIC']\n        cflags.remove('-fno-rtti')  # Needed for above pedanticness.\n    else:\n        cflags += ['-O2', '-DNDEBUG']\n    try:\n        proc = subprocess.Popen(\n            [CXX, '-fdiagnostics-color', '-c', '-x', 'c++', '/dev/null',\n             '-o', '/dev/null'],\n            stdout=open(os.devnull, 'wb'), stderr=subprocess.STDOUT)\n        if proc.wait() == 0:\n            cflags += ['-fdiagnostics-color']\n    except Exception:\n        pass\n    if platform.is_mingw():\n        cflags += ['-D_WIN32_WINNT=0x0601', '-D__USE_MINGW_ANSI_STDIO=1']\n    ldflags = ['-L$builddir']\n    if platform.uses_usr_local():\n        cflags.append('-I/usr/local/include')\n        ldflags.append('-L/usr/local/lib')\n    if platform.is_aix():\n        # printf formats for int64_t, uint64_t; large file support\n        cflags.append('-D__STDC_FORMAT_MACROS')\n        cflags.append('-D_LARGE_FILES')\n\n\nlibs = []\n\nif platform.is_mingw():\n    cflags.remove('-fvisibility=hidden');\n    ldflags.append('-static')\nelif platform.is_solaris():\n    cflags.remove('-fvisibility=hidden')\nelif platform.is_aix():\n    cflags.remove('-fvisibility=hidden')\nelif platform.is_msvc():\n    pass\nelse:\n    if options.profile == 'gmon':\n        cflags.append('-pg')\n        ldflags.append('-pg')\n    elif options.profile == 'pprof':\n        cflags.append('-fno-omit-frame-pointer')\n        libs.extend(['-Wl,--no-as-needed', '-lprofiler'])\n\nif platform.supports_ppoll() and not options.force_pselect:\n    cflags.append('-DUSE_PPOLL')\nif platform.supports_ninja_browse():\n    cflags.append('-DNINJA_HAVE_BROWSE')\n\n# Search for generated headers relative to build dir.\ncflags.append('-I.')\n\ndef shell_escape(str: str) -> str:\n    \"\"\"Escape str such that it's interpreted as a single argument by\n    the shell.\"\"\"\n\n    # This isn't complete, but it's just enough to make NINJA_PYTHON work.\n    if platform.is_windows():\n      return str\n    if '\"' in str:\n        return \"'%s'\" % str.replace(\"'\", \"\\\\'\")\n    return str\n\nif 'CFLAGS' in configure_env:\n    cflags.append(configure_env['CFLAGS'])\n    ldflags.append(configure_env['CFLAGS'])\nif 'CXXFLAGS' in configure_env:\n    cflags.append(configure_env['CXXFLAGS'])\n    ldflags.append(configure_env['CXXFLAGS'])\nn.variable('cflags', ' '.join(shell_escape(flag) for flag in cflags))\nif 'LDFLAGS' in configure_env:\n    ldflags.append(configure_env['LDFLAGS'])\nn.variable('ldflags', ' '.join(shell_escape(flag) for flag in ldflags))\n\nn.newline()\n\nif platform.is_msvc():\n    n.rule('cxx',\n        command='$cxx $cflags -c $in /Fo$out /Fd' + built('$pdb'),\n        description='CXX $out',\n        deps='msvc'  # /showIncludes is included in $cflags.\n    )\nelse:\n    n.rule('cxx',\n        command='$cxx -MMD -MT $out -MF $out.d $cflags -c $in -o $out',\n        depfile='$out.d',\n        deps='gcc',\n        description='CXX $out')\nn.newline()\n\nif host.is_msvc():\n    n.rule('ar',\n           command='lib /nologo /ltcg /out:$out $in',\n           description='LIB $out')\nelif host.is_mingw():\n    n.rule('ar',\n           command='$ar crs $out $in',\n           description='AR $out')\nelse:\n    n.rule('ar',\n           command='rm -f $out && $ar crs $out $in',\n           description='AR $out')\nn.newline()\n\nif platform.is_msvc():\n    n.rule('link',\n        command='$cxx $in $libs /nologo /link $ldflags /out:$out',\n        description='LINK $out')\nelse:\n    n.rule('link',\n        command='$cxx $ldflags -o $out $in $libs',\n        description='LINK $out')\nn.newline()\n\nobjs = []\n\nif platform.supports_ninja_browse():\n    n.comment('browse_py.h is used to inline browse.py.')\n    n.rule('inline',\n           command='\"%s\"' % src('inline.sh') + ' $varname < $in > $out',\n           description='INLINE $out')\n    n.build(built('browse_py.h'), 'inline', src('browse.py'),\n            implicit=src('inline.sh'),\n            variables=[('varname', 'kBrowsePy')])\n    n.newline()\n\n    objs += cxx('browse', order_only=built('browse_py.h'))\n    n.newline()\n\nn.comment('the depfile parser and ninja lexers are generated using re2c.')\ndef has_re2c() -> bool:\n    try:\n        proc = subprocess.Popen(['re2c', '-V'], stdout=subprocess.PIPE)\n        return int(proc.communicate()[0], 10) >= 1503\n    except OSError:\n        return False\nif has_re2c():\n    n.rule('re2c',\n           command='re2c -b -i --no-generation-date --no-version -o $out $in',\n           description='RE2C $out')\n    # Generate the .cc files in the source directory so we can check them in.\n    n.build(src('depfile_parser.cc'), 're2c', src('depfile_parser.in.cc'))\n    n.build(src('lexer.cc'), 're2c', src('lexer.in.cc'))\nelse:\n    print(\"warning: A compatible version of re2c (>= 0.15.3) was not found; \"\n           \"changes to src/*.in.cc will not affect your build.\")\nn.newline()\n\ncxxvariables = []\nif platform.is_msvc():\n    cxxvariables = [('pdb', 'ninja.pdb')]\n\nn.comment('Generate a library for `ninja-re2c`.')\nre2c_objs = []\nfor name in ['depfile_parser', 'lexer']:\n    re2c_objs += cxx(name, variables=cxxvariables)\nif platform.is_msvc():\n    n.build(built('ninja-re2c.lib'), 'ar', re2c_objs)\nelse:\n    n.build(built('libninja-re2c.a'), 'ar', re2c_objs)\nn.newline()\n\nn.comment('Core source files all build into ninja library.')\nobjs.extend(re2c_objs)\nfor name in ['build',\n             'build_log',\n             'clean',\n             'clparser',\n             'debug_flags',\n             'deps_log',\n             'disk_interface',\n             'dyndep',\n             'dyndep_parser',\n             'edit_distance',\n             'elide_middle',\n             'eval_env',\n             'graph',\n             'graphviz',\n             'jobserver',\n             'json',\n             'line_printer',\n             'manifest_parser',\n             'metrics',\n             'missing_deps',\n             'parser',\n             'real_command_runner',\n             'state',\n             'status_printer',\n             'string_piece_util',\n             'util',\n             'version']:\n    objs += cxx(name, variables=cxxvariables)\nif platform.is_windows():\n    for name in ['subprocess-win32',\n                 'includes_normalize-win32',\n                 'jobserver-win32',\n                 'msvc_helper-win32',\n                 'msvc_helper_main-win32']:\n        objs += cxx(name, variables=cxxvariables)\n    if platform.is_msvc():\n        objs += cxx('minidump-win32', variables=cxxvariables)\n    objs += cc('getopt')\nelse:\n    for name in ['jobserver-posix',\n                 'subprocess-posix']:\n        objs += cxx(name, variables=cxxvariables)\nif platform.is_aix():\n    objs += cc('getopt')\nif platform.is_msvc():\n    ninja_lib = n.build(built('ninja.lib'), 'ar', objs)\nelse:\n    ninja_lib = n.build(built('libninja.a'), 'ar', objs)\nn.newline()\n\nif platform.is_msvc():\n    libs.append('ninja.lib')\nelse:\n    libs.append('-lninja')\n\nif platform.is_aix() and not platform.is_os400_pase():\n    libs.append('-lperfstat')\n\nall_targets = []\n\nn.comment('Main executable is library plus main() function.')\nobjs = cxx('ninja', variables=cxxvariables)\nninja = n.build(binary('ninja'), 'link', objs, implicit=ninja_lib,\n                variables=[('libs', libs)])\nn.newline()\nall_targets += ninja\n\nif options.bootstrap:\n    # We've built the ninja binary.  Don't run any more commands\n    # through the bootstrap executor, but continue writing the\n    # build.ninja file.\n    n = ninja_writer\n\n# Build the ninja_test executable only if the GTest source directory\n# is provided explicitly. Either from the environment with GTEST_SOURCE_DIR\n# or with the --gtest-source-dir command-line option.\n#\n# Do not try to look for an installed binary version, and link against it\n# because doing so properly is platform-specific (use the CMake build for\n# this).\nif options.gtest_source_dir:\n    gtest_src_dir = options.gtest_source_dir\nelse:\n    gtest_src_dir = os.environ.get('GTEST_SOURCE_DIR')\n\nif gtest_src_dir:\n    # Verify GoogleTest source directory, and add its include directory\n    # to the global include search path (even for non-test sources) to\n    # keep the build plan generation simple.\n    gtest_all_cc = os.path.join(gtest_src_dir, 'googletest', 'src', 'gtest-all.cc')\n    if not os.path.exists(gtest_all_cc):\n        print('ERROR: Missing GoogleTest source file: %s' % gtest_all_cc)\n        sys.exit(1)\n\n    n.comment('Tests all build into ninja_test executable.')\n\n    # Test-specific version of cflags, must include the GoogleTest\n    # include directory.\n    test_cflags = cflags.copy()\n    test_cflags.append('-I' + os.path.join(gtest_src_dir, 'googletest', 'include'))\n\n    test_variables = [('cflags', test_cflags)]\n    if platform.is_msvc():\n        test_variables += [('pdb', 'ninja_test.pdb')]\n\n    test_names = [\n        'build_log_test',\n        'build_test',\n        'clean_test',\n        'clparser_test',\n        'depfile_parser_test',\n        'deps_log_test',\n        'disk_interface_test',\n        'dyndep_parser_test',\n        'edit_distance_test',\n        'elide_middle_test',\n        'explanations_test',\n        'graph_test',\n        'jobserver_test',\n        'json_test',\n        'lexer_test',\n        'manifest_parser_test',\n        'ninja_test',\n        'state_test',\n        'string_piece_util_test',\n        'subprocess_test',\n        'test',\n        'util_test',\n    ]\n    if platform.is_windows():\n        test_names += [\n            'includes_normalize_test',\n            'msvc_helper_test',\n        ]\n\n    objs = []\n    for name in test_names:\n        objs += cxx(name, variables=test_variables)\n\n    # Build GTest as a monolithic source file.\n    # This requires one extra include search path, so replace the\n    # value of 'cflags' in our list.\n    gtest_all_variables = test_variables[1:] + [\n      ('cflags', test_cflags + ['-I' + os.path.join(gtest_src_dir, 'googletest') ]),\n    ]\n    # Do not use cxx() directly to ensure the object file is under $builddir.\n    objs += n.build(built('gtest_all' + objext), 'cxx', gtest_all_cc, variables=gtest_all_variables)\n\n    ninja_test = n.build(binary('ninja_test'), 'link', objs, implicit=ninja_lib,\n                         variables=[('libs', libs)])\n    n.newline()\n    all_targets += ninja_test\n\nn.comment('Ancillary executables.')\n\nif platform.is_aix() and '-maix64' not in ldflags:\n    # Both hash_collision_bench and manifest_parser_perftest require more\n    # memory than will fit in the standard 32-bit AIX shared stack/heap (256M)\n    libs.append('-Wl,-bmaxdata:0x80000000')\n\nfor name in ['build_log_perftest',\n             'canon_perftest',\n             'elide_middle_perftest',\n             'depfile_parser_perftest',\n             'hash_collision_bench',\n             'manifest_parser_perftest',\n             'clparser_perftest']:\n  if platform.is_msvc():\n    cxxvariables = [('pdb', name + '.pdb')]\n  objs = cxx(name, variables=cxxvariables)\n  all_targets += n.build(binary(name), 'link', objs,\n                         implicit=ninja_lib, variables=[('libs', libs)])\n\nn.newline()\n\nn.comment('Generate a graph using the \"graph\" tool.')\nn.rule('gendot',\n       command='./ninja -t graph all > $out')\nn.rule('gengraph',\n       command='dot -Tpng $in > $out')\ndot = n.build(built('graph.dot'), 'gendot', ['ninja', 'build.ninja'])\nn.build('graph.png', 'gengraph', dot)\nn.newline()\n\nn.comment('Generate the manual using asciidoc.')\nn.rule('asciidoc',\n       command='asciidoc -b docbook -d book -o $out $in',\n       description='ASCIIDOC $out')\nn.rule('xsltproc',\n       command='xsltproc --nonet doc/docbook.xsl $in > $out',\n       description='XSLTPROC $out')\ndocbookxml = n.build(built('manual.xml'), 'asciidoc', doc('manual.asciidoc'))\nmanual = n.build(doc('manual.html'), 'xsltproc', docbookxml,\n                 implicit=[doc('style.css'), doc('docbook.xsl')])\nn.build('manual', 'phony',\n        order_only=manual)\nn.newline()\n\nn.rule('dblatex',\n       command='dblatex -q -o $out -p doc/dblatex.xsl $in',\n       description='DBLATEX $out')\nn.build(doc('manual.pdf'), 'dblatex', docbookxml,\n        implicit=[doc('dblatex.xsl')])\n\nn.comment('Generate Doxygen.')\nn.rule('doxygen',\n       command='doxygen $in',\n       description='DOXYGEN $in')\nn.variable('doxygen_mainpage_generator',\n           src('gen_doxygen_mainpage.sh'))\nn.rule('doxygen_mainpage',\n       command='$doxygen_mainpage_generator $in > $out',\n       description='DOXYGEN_MAINPAGE $out')\nmainpage = n.build(built('doxygen_mainpage'), 'doxygen_mainpage',\n                   ['README.md', 'COPYING'],\n                   implicit=['$doxygen_mainpage_generator'])\nn.build('doxygen', 'doxygen', doc('doxygen.config'),\n        implicit=mainpage)\nn.newline()\n\nif not host.is_mingw():\n    n.comment('Regenerate build files if build script changes.')\n    n.rule('configure',\n           command='${configure_env}%s $root/configure.py $configure_args' %\n               options.with_python,\n           generator=True)\n    n.build('build.ninja', 'configure',\n            implicit=['$root/configure.py',\n                      os.path.normpath('$root/misc/ninja_syntax.py')])\n    n.newline()\n\nn.default(ninja)\nn.newline()\n\nif host.is_linux():\n    n.comment('Packaging')\n    n.rule('rpmbuild',\n           command=\"misc/packaging/rpmbuild.sh\",\n           description='Building rpms..')\n    n.build('rpm', 'rpmbuild')\n    n.newline()\n\nn.build('all', 'phony', all_targets)\n\nn.close()  # type: ignore # Item \"Bootstrap\" of \"Writer | Bootstrap\" has no attribute \"close\"\nprint('wrote %s.' % BUILD_FILENAME)\n\nif options.bootstrap:\n    print('bootstrap complete.  rebuilding...')\n\n    rebuild_args = []\n\n    if platform.can_rebuild_in_place():\n        rebuild_args.append('./ninja')\n    else:\n        if platform.is_windows():\n            bootstrap_exe = 'ninja.bootstrap.exe'\n            final_exe = 'ninja.exe'\n        else:\n            bootstrap_exe = './ninja.bootstrap'\n            final_exe = './ninja'\n\n        if os.path.exists(bootstrap_exe):\n            os.unlink(bootstrap_exe)\n        os.rename(final_exe, bootstrap_exe)\n\n        rebuild_args.append(bootstrap_exe)\n\n    if options.verbose:\n        rebuild_args.append('-v')\n\n    subprocess.check_call(rebuild_args)\n"
  },
  {
    "path": "doc/README.md",
    "content": "This directory contains the Ninja manual and support files used in\nbuilding it.  Here's a brief overview of how it works.\n\nThe source text, `manual.asciidoc`, is written in the AsciiDoc format.\nAsciiDoc can generate HTML but it doesn't look great; instead, we use\nAsciiDoc to generate the Docbook XML format and then provide our own\nDocbook XSL tweaks to produce HTML from that.\n\nIn theory using AsciiDoc and DocBook allows us to produce nice PDF\ndocumentation etc.  In reality it's not clear anyone wants that, but the\nbuild rules are in place to generate it if you install dblatex.\n"
  },
  {
    "path": "doc/dblatex.xsl",
    "content": "<!-- This custom XSL tweaks the dblatex XML settings. -->\n<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>\n  <!-- These parameters disable the list of collaborators and revisions.\n       Together remove a useless page from the front matter. -->\n  <xsl:param name='doc.collab.show'>0</xsl:param>\n  <xsl:param name='latex.output.revhistory'>0</xsl:param>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/docbook.xsl",
    "content": "<!-- This custom XSL tweaks the DocBook XML -> HTML settings to produce\n     an OK-looking manual.  -->\n<!DOCTYPE xsl:stylesheet [\n<!ENTITY css SYSTEM \"style.css\">\n]>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n\t\tversion='1.0'>\n  <xsl:import href=\"http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl\"/>\n\n  <!-- Embed our stylesheet as the user-provided <head> content. -->\n  <xsl:template name=\"user.head.content\"><style>&css;</style></xsl:template>\n\n  <!-- Remove the body.attributes block, which specifies a bunch of\n       useless bgcolor etc. attrs on the <body> tag. -->\n  <xsl:template name=\"body.attributes\"></xsl:template>\n\n  <!-- Specify that in \"book\" form (which we're using), we only want a\n       single table of contents at the beginning of the document. -->\n  <xsl:param name=\"generate.toc\">book toc</xsl:param>\n\n  <!-- Don't put the \"Chapter 1.\" prefix on the \"chapters\". -->\n  <xsl:param name=\"chapter.autolabel\">0</xsl:param>\n\n  <!-- Make builds reproducible by generating the same IDs from the same inputs -->\n  <xsl:param name=\"generate.consistent.ids\">1</xsl:param>\n\n  <!-- Use <ul> for the table of contents.  By default DocBook uses a\n       <dl>, which makes no semantic sense.  I imagine they just did\n       it because it looks nice? -->\n  <xsl:param name=\"toc.list.type\">ul</xsl:param>\n\n  <xsl:output method=\"html\" encoding=\"utf-8\" indent=\"no\"\n              doctype-public=\"\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/doxygen.config",
    "content": "# Doxyfile 1.4.5\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project\n#\n# All text after a hash (#) is considered a comment and will be ignored\n# The format is:\n#       TAG = value [value, ...]\n# For lists items can also be appended using:\n#       TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\" \")\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded\n# by quotes) that should identify the project.\n\nPROJECT_NAME           = \"Ninja\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number.\n# This could be handy for archiving the generated documentation or\n# if some version control system is used.\n\n# PROJECT_NUMBER         = \"0\"\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)\n# base path where the generated documentation will be put.\n# If a relative path is entered, it will be relative to the location\n# where doxygen was started. If left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = \"doc/doxygen/\"\n\n# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create\n# 4096 sub-directories (in 2 levels) under the output directory of each output\n# format and will distribute the generated files over these directories.\n# Enabling this option can be useful when feeding doxygen a huge amount of\n# source files, where putting all generated files in the same directory would\n# otherwise cause performance problems for the file system.\n\nCREATE_SUBDIRS         = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# The default language is English, other supported languages are:\n# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish,\n# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese,\n# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian,\n# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish,\n# Swedish, and Ukrainian.\n\nOUTPUT_LANGUAGE        = English\n\n# This tag can be used to specify the encoding used in the generated output.\n# The encoding is not always determined by the language that is chosen,\n# but also whether or not the output is meant for Windows or non-Windows users.\n# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES\n# forces the Windows encoding (this is the default for the Windows binary),\n# whereas setting the tag to NO uses a Unix-style encoding (the default for\n# all platforms other than Windows).\n\n# Obsolet option.\n#USE_WINDOWS_ENCODING   = YES\n\n# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will\n# include brief member descriptions after the members that are listed in\n# the file and class documentation (similar to JavaDoc).\n# Set to NO to disable this.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend\n# the brief description of a member or function before the detailed description.\n# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator\n# that is used to form the text in various listings. Each string\n# in this list, if found as the leading text of the brief description, will be\n# stripped from the text and the result after processing the whole list, is\n# used as the annotated text. Otherwise, the brief description is used as-is.\n# If left blank, the following values are used (\"$name\" is automatically\n# replaced with the name of the entity): \"The $name class\" \"The $name widget\"\n# \"The $name file\" \"is\" \"provides\" \"specifies\" \"contains\"\n# \"represents\" \"a\" \"an\" \"the\"\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# Doxygen will generate a detailed section even if there is only a brief\n# description.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n\nINLINE_INHERITED_MEMB  = YES\n\n# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full\n# path before files name in the file list and in the header files. If set\n# to NO the shortest path that makes the file name unique will be used.\n\nFULL_PATH_NAMES        = YES\n\n# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag\n# can be used to strip a user-defined part of the path. Stripping is\n# only done if one of the specified strings matches the left-hand part of\n# the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the\n# path to strip.\n\nSTRIP_FROM_PATH        = src\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of\n# the path mentioned in the documentation of a class, which tells\n# the reader which header file to include in order to use a class.\n# If left blank only the name of the header file containing the class\n# definition is used. Otherwise one should specify the include paths that\n# are normally passed to the compiler using the -I flag.\n\nSTRIP_FROM_INC_PATH    = src/\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter\n# (but less readable) file names. This can be useful is your file systems\n# doesn't support long names like on DOS, Mac, or CD-ROM.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen\n# will interpret the first line (until the first dot) of a JavaDoc-style\n# comment as the brief description. If set to NO, the JavaDoc\n# comments will behave just like the Qt-style comments (thus requiring an\n# explicit @brief command for a brief description.\n\nJAVADOC_AUTOBRIEF      = YES\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen\n# treat a multi-line C++ special comment block (i.e. a block of //! or ///\n# comments) as a brief description. This used to be the default behaviour.\n# The new default is to treat a multi-line C++ comment block as a detailed\n# description. Set this tag to YES if you prefer the old behaviour instead.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the DETAILS_AT_TOP tag is set to YES then Doxygen\n# will output the detailed description near the top, like JavaDoc.\n# If set to NO, the detailed description appears after the member\n# documentation.\n\n# Has become obsolete.\n#DETAILS_AT_TOP         = NO\n\n# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented\n# member inherits the documentation from any documented member that it\n# re-implements.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce\n# a new page for each member. If set to NO, the documentation of a member will\n# be part of the file/class/namespace that contains it.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab.\n# Doxygen uses this value to replace tabs by spaces in code fragments.\n\nTAB_SIZE               = 2\n\n# This tag can be used to specify a number of aliases that acts\n# as commands in the documentation. An alias has the form \"name=value\".\n# For example adding \"sideeffect=\\par Side Effects:\\n\" will allow you to\n# put the command \\sideeffect (or @sideeffect) in the documentation, which\n# will result in a user-defined paragraph with heading \"Side Effects:\".\n# You can put \\n's in the value part of an alias to insert newlines.\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C\n# sources only. Doxygen will then generate output that is more tailored for C.\n# For instance, some of the names that are used will be different. The list\n# of all members will be omitted, etc.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java\n# sources only. Doxygen will then generate output that is more tailored for Java.\n# For instance, namespaces will be presented as packages, qualified scopes\n# will look different, etc.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to\n# include (a tag file for) the STL sources as input, then you should\n# set this tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.\n# func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n\n# BUILTIN_STL_SUPPORT    = NO\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES, then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# Set the SUBGROUPING tag to YES (the default) to allow class member groups of\n# the same type (for instance a group of public functions) to be put as a\n# subgroup of that type (e.g. under the Public Functions section). Set it to\n# NO to prevent subgrouping. Alternatively, this can be done per class using\n# the \\nosubgrouping command.\n\nSUBGROUPING            = YES\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in\n# documentation are documented, even if no documentation was available.\n# Private class members and static file members will be hidden unless\n# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES all private members of a class\n# will be included in the documentation.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_STATIC tag is set to YES all static members of a file\n# will be included in the documentation.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)\n# defined locally in source files will be included in the documentation.\n# If set to NO only classes defined in header files are included.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. When set to YES local\n# methods, which are defined in the implementation section but not in\n# the interface are included in the documentation.\n# If set to NO (the default) only methods in the interface are included.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all\n# undocumented members of documented classes, files or namespaces.\n# If set to NO (the default) these members will be included in the\n# various overviews, but no documentation section is generated.\n# This option has no effect if EXTRACT_ALL is enabled.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy.\n# If set to NO (the default) these classes will be included in the various\n# overviews. This option has no effect if EXTRACT_ALL is enabled.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all\n# friend (class|struct|union) declarations.\n# If set to NO (the default) these declarations will be included in the\n# documentation.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any\n# documentation blocks found inside the body of a function.\n# If set to NO (the default) these blocks will be appended to the\n# function's detailed documentation block.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation\n# that is typed after a \\internal command is included. If the tag is set\n# to NO (the default) then the documentation will be excluded.\n# Set it to YES to include the internal documentation.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate\n# file names in lower-case letters. If set to YES upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen\n# will show members with their full class and namespace scopes in the\n# documentation. If set to YES the scope will be hidden.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen\n# will put a list of the files that are included by a file in the documentation\n# of that file.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]\n# is inserted in the documentation for inline members.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen\n# will sort the (detailed) documentation of file and class members\n# alphabetically by member name. If set to NO the members will appear in\n# declaration order.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the\n# brief documentation of file, namespace and class members alphabetically\n# by member name. If set to NO (the default) the members will appear in\n# declaration order.\n\nSORT_BRIEF_DOCS        = YES\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be\n# sorted by fully-qualified names, including namespaces. If set to\n# NO (the default), the class list will be sorted only by class name,\n# not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the\n# alphabetical list.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or\n# disable (NO) the todo list. This list is created by putting \\todo\n# commands in the documentation.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or\n# disable (NO) the test list. This list is created by putting \\test\n# commands in the documentation.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or\n# disable (NO) the bug list. This list is created by putting \\bug\n# commands in the documentation.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or\n# disable (NO) the deprecated list. This list is created by putting\n# \\deprecated commands in the documentation.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional\n# documentation sections, marked by \\if sectionname ... \\endif.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines\n# the initial value of a variable or define consists of for it to appear in\n# the documentation. If the initializer consists of more lines than specified\n# here it will be hidden. Use a value of 0 to hide initializers completely.\n# The appearance of the initializer of individual variables and defines in the\n# documentation can be controlled using \\showinitializer or \\hideinitializer\n# command in the documentation regardless of this setting.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated\n# at the bottom of the documentation of classes and structs. If set to YES the\n# list will mention the files that were used to generate the documentation.\n\nSHOW_USED_FILES        = YES\n\n# If the sources in your project are distributed over multiple directories\n# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy\n# in the documentation. The default is YES.\n\nSHOW_DIRECTORIES       = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from the\n# version control system). Doxygen will invoke the program by executing (via\n# popen()) the command <command> <input-file>, where <command> is the value of\n# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file\n# provided by doxygen. Whatever the program writes to standard output\n# is used as the file version. See the manual for examples.\n\nFILE_VERSION_FILTER    =\n\n#---------------------------------------------------------------------------\n# configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated\n# by doxygen. Possible values are YES and NO. If left blank NO is used.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated by doxygen. Possible values are YES and NO. If left blank\n# NO is used.\n\nWARNINGS               = YES\n\n# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings\n# for undocumented members. If EXTRACT_ALL is set to YES then this flag will\n# automatically be disabled.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some\n# parameters in a documented function, or documenting parameters that\n# don't exist or using markup commands wrongly.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be abled to get warnings for\n# functions that are documented, but have no documentation for their parameters\n# or return value. If set to NO (the default) doxygen will only warn about\n# wrong or incomplete parameter documentation, but not about the absence of\n# documentation.\n\nWARN_NO_PARAMDOC       = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that\n# doxygen can produce. The string should contain the $file, $line, and $text\n# tags, which will be replaced by the file and line number from which the\n# warning originated and the warning text. Optionally the format may contain\n# $version, which will be replaced by the version of the file (if it could\n# be obtained via FILE_VERSION_FILTER)\n\nWARN_FORMAT            = \"$file:$line: $text \"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning\n# and error messages should be written. If left blank the output is written\n# to stderr.\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag can be used to specify the files and/or directories that contain\n# documented source files. You may enter file names like \"myfile.cpp\" or\n# directories like \"/usr/src/myproject\". Separate the files or directories\n# with spaces.\n\nINPUT                  = src \\\n                         build/doxygen_mainpage\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp\n# and *.h) to filter out the source-files in the directories. If left\n# blank the following patterns are tested:\n# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx\n# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py\n\nFILE_PATTERNS          = *.cc \\\n                         *.h\n\n# The RECURSIVE tag can be used to turn specify whether or not subdirectories\n# should be searched for input files as well. Possible values are YES and NO.\n# If left blank NO is used.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used select whether or not files or\n# directories that are symbolic links (a Unix filesystem feature) are excluded\n# from the input.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories. Note that the wildcards are matched\n# against the file with absolute path, so to exclude all test directories\n# for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or\n# directories that contain example code fragments that are included (see\n# the \\include command).\n\nEXAMPLE_PATH           = src\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp\n# and *.h) to filter out the source-files in the directories. If left\n# blank all files are included.\n\nEXAMPLE_PATTERNS       = *.cpp \\\n                         *.cc \\\n                         *.h \\\n                         *.hh \\\n                         INSTALL DEPENDENCIES CHANGELOG LICENSE LGPL\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude\n# commands irrespective of the value of the RECURSIVE tag.\n# Possible values are YES and NO. If left blank NO is used.\n\nEXAMPLE_RECURSIVE      = YES\n\n# The IMAGE_PATH tag can be used to specify one or more files or\n# directories that contain image that are included in the documentation (see\n# the \\image command).\n\nIMAGE_PATH             = src\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command <filter> <input-file>, where <filter>\n# is the value of the INPUT_FILTER tag, and <input-file> is the name of an\n# input file. Doxygen will then use the output that the filter program writes\n# to standard output.  If FILTER_PATTERNS is specified, this tag will be\n# ignored.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis.  Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match.  The filters are a list of the form:\n# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further\n# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER\n# is applied to all files.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will be used to filter the input files when producing source\n# files to browse (i.e. when SOURCE_BROWSER is set to YES).\n\nFILTER_SOURCE_FILES    = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will\n# be generated. Documented entities will be cross-referenced with these sources.\n# Note: To get rid of all source code in the generated output, make sure also\n# VERBATIM_HEADERS is set to NO.\n\nSOURCE_BROWSER         = YES\n\n# Setting the INLINE_SOURCES tag to YES will include the body\n# of functions and classes directly in the documentation.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct\n# doxygen to hide any special comment blocks from generated source code\n# fragments. Normal C and C++ comments will always remain visible.\n\nSTRIP_CODE_COMMENTS    = NO\n\n# If the REFERENCED_BY_RELATION tag is set to YES (the default)\n# then for each documented function all documented\n# functions referencing it will be listed.\n\nREFERENCED_BY_RELATION = YES\n\n# If the REFERENCES_RELATION tag is set to YES (the default)\n# then for each documented function all documented entities\n# called/used by that function will be listed.\n\nREFERENCES_RELATION    = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code\n# will point to the HTML generated by the htags(1) tool instead of doxygen\n# built-in source browser. The htags tool is part of GNU's global source\n# tagging system (see http://www.gnu.org/software/global/global.html). You\n# will need version 4.8.6 or higher.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen\n# will generate a verbatim copy of the header file for each class for\n# which an include is specified. Set to NO to disable this.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index\n# of all compounds will be generated. Enable this if the project\n# contains a lot of classes, structs, unions or interfaces.\n\nALPHABETICAL_INDEX     = YES\n\n# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then\n# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns\n# in which this list will be split (can be a number in the range [1..20])\n\nCOLS_IN_ALPHA_INDEX    = 2\n\n# In case all classes in a project start with a common prefix, all\n# classes will be put under the same header in the alphabetical index.\n# The IGNORE_PREFIX tag can be used to specify one or more prefixes that\n# should be ignored while generating the index headers.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES (the default) Doxygen will\n# generate HTML output.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `html' will be used as the default path.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for\n# each generated HTML page (for example: .htm,.php,.asp). If it is left blank\n# doxygen will generate files with .html extension.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a personal HTML header for\n# each generated HTML page. If it is left blank doxygen will generate a\n# standard header.\nHTML_HEADER            =\n\n\n# The HTML_FOOTER tag can be used to specify a personal HTML footer for\n# each generated HTML page. If it is left blank doxygen will generate a\n# standard footer.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading\n# style sheet that is used by each HTML page. It can be used to\n# fine-tune the look of the HTML output. If the tag is left blank doxygen\n# will generate a default style sheet. Note that doxygen will try to copy\n# the style sheet file to the HTML output directory, so don't put your own\n# stylesheet in the HTML output directory as well, or it will be erased!\n\nHTML_STYLESHEET        =\n\n# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,\n# files or namespaces will be aligned in HTML using tables. If set to\n# NO a bullet list will be used.\n\nHTML_ALIGN_MEMBERS     = YES\n\n# If the GENERATE_HTMLHELP tag is set to YES, additional index files\n# will be generated that can be used as input for tools like the\n# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)\n# of the generated HTML documentation.\n\nGENERATE_HTMLHELP      = YES\n\n# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can\n# be used to specify the file name of the resulting .chm file. You\n# can add a path in front of the file if the result should not be\n# written to the html output directory.\n\nCHM_FILE               =\n\n# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can\n# be used to specify the location (absolute path including file name) of\n# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run\n# the HTML help compiler on the generated index.hhp.\n\nHHC_LOCATION           =\n\n# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag\n# controls if a separate .chi index file is generated (YES) or that\n# it should be included in the master .chm file (NO).\n\nGENERATE_CHI           = NO\n\n# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag\n# controls whether a binary table of contents is generated (YES) or a\n# normal table of contents (NO) in the .chm file.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members\n# to the contents of the HTML help documentation and to the tree view.\n\nTOC_EXPAND             = NO\n\n# The DISABLE_INDEX tag can be used to turn on/off the condensed index at\n# top of each HTML page. The value NO (the default) enables the index and\n# the value YES disables it.\n\nDISABLE_INDEX          = NO\n\n# This tag can be used to set the number of enum values (range [1..20])\n# that doxygen will group on one line in the generated HTML documentation.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be\n# generated containing a tree-like index structure (just like the one that\n# is generated for HTML Help). For this to work a browser that supports\n# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,\n# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are\n# probably better off using the HTML help feature.\n\nGENERATE_TREEVIEW      = YES\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be\n# used to set the initial width (in pixels) of the frame in which the tree\n# is shown.\n\nTREEVIEW_WIDTH         = 250\n\n#---------------------------------------------------------------------------\n# configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will\n# generate Latex output.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `latex' will be used as the default path.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked. If left blank `latex' will be used as the default command name.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to\n# generate index for LaTeX. If left blank `makeindex' will be used as the\n# default command name.\n\nMAKEINDEX_CMD_NAME     =\n\n# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact\n# LaTeX documents. This may be useful for small projects and may help to\n# save some trees in general.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used\n# by the printer. Possible values are: a4, a4wide, letter, legal and\n# executive. If left blank a4wide will be used.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX\n# packages that should be included in the LaTeX output.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for\n# the generated latex document. The header should contain everything until\n# the first chapter. If it is left blank doxygen will generate a\n# standard header. Notice: only use this tag if you know what you are doing!\n\nLATEX_HEADER           =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated\n# is prepared for conversion to pdf (using ps2pdf). The pdf file will\n# contain links (just like the HTML output) instead of page references\n# This makes the output suitable for online browsing using a pdf viewer.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of\n# plain latex in the generated Makefile. Set this option to YES to get a\n# higher quality PDF documentation.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\\\batchmode.\n# command to the generated LaTeX files. This will instruct LaTeX to keep\n# running if errors occur, instead of asking the user for help.\n# This option is also used when generating formulas in HTML.\n\nLATEX_BATCHMODE        = YES\n\n# If LATEX_HIDE_INDICES is set to YES then doxygen will not\n# include the index chapters (such as File Index, Compound Index, etc.)\n# in the output.\n\nLATEX_HIDE_INDICES     = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output\n# The RTF output is optimized for Word 97 and may not look very pretty with\n# other RTF readers or editors.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `rtf' will be used as the default path.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES Doxygen generates more compact\n# RTF documents. This may be useful for small projects and may help to\n# save some trees in general.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated\n# will contain hyperlink fields. The RTF file will\n# contain links (just like the HTML output) instead of page references.\n# This makes the output suitable for online browsing using WORD or other\n# programs which support those fields.\n# Note: wordpad (write) and others do not support links.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# config file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an rtf document.\n# Syntax is similar to doxygen's config file.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES (the default) Doxygen will\n# generate man pages\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `man' will be used as the default path.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to\n# the generated man pages (default is the subroutine's section .3)\n\nMAN_EXTENSION          = .3\n\n# If the MAN_LINKS tag is set to YES and Doxygen generates man output,\n# then it will generate one additional man file for each entity\n# documented in the real man page(s). These additional files\n# only source the real man page, but without them the man command\n# would be unable to find the correct page. The default is NO.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES Doxygen will\n# generate an XML file that captures the structure of\n# the code including all documentation.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be\n# put in front of it. If left blank `xml' will be used as the default path.\n\nXML_OUTPUT             = xml\n\n# The XML_SCHEMA tag can be used to specify an XML schema,\n# which can be used by a validating XML parser to check the\n# syntax of the XML files.\n\nXML_SCHEMA             =\n\n# The XML_DTD tag can be used to specify an XML DTD,\n# which can be used by a validating XML parser to check the\n# syntax of the XML files.\n\nXML_DTD                =\n\n# If the XML_PROGRAMLISTING tag is set to YES Doxygen will\n# dump the program listings (including syntax highlighting\n# and cross-referencing information) to the XML output. Note that\n# enabling this will significantly increase the size of the XML output.\n\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will\n# generate an AutoGen Definitions (see autogen.sf.net) file\n# that captures the structure of the code including all\n# documentation. Note that this feature is still experimental\n# and incomplete at the moment.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES Doxygen will\n# generate a Perl module file that captures the structure of\n# the code including all documentation. Note that this\n# feature is still experimental and incomplete at the\n# moment.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES Doxygen will generate\n# the necessary Makefile rules, Perl scripts and LaTeX code to be able\n# to generate PDF and DVI output from the Perl module output.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be\n# nicely formatted so it can be parsed by a human reader.  This is useful\n# if you want to understand what is going on.  On the other hand, if this\n# tag is set to NO the size of the Perl module output will be much smaller\n# and Perl will parse it just the same.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file\n# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.\n# This is useful so different doxyrules.make files included by the same\n# Makefile don't overwrite each other's variables.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will\n# evaluate all C-preprocessor directives found in the sources and include\n# files.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro\n# names in the source code. If set to NO (the default) only conditional\n# compilation will be performed. Macro expansion can be done in a controlled\n# way by setting EXPAND_ONLY_PREDEF to YES.\n\nMACRO_EXPANSION        = YES\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES\n# then the macro expansion is limited to the macros specified with the\n# PREDEFINED and EXPAND_AS_DEFINED tags.\n\nEXPAND_ONLY_PREDEF     = YES\n\n# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files\n# in the INCLUDE_PATH (see below) will be search if a #include is found.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by\n# the preprocessor.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will\n# be used.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that\n# are defined before the preprocessor is started (similar to the -D option of\n# gcc). The argument of the tag is a list of macros of the form: name\n# or name=definition (no spaces). If the definition and the = are\n# omitted =1 is assumed. To prevent a macro definition from being\n# undefined via #undef or recursively expanded use the := operator\n# instead of the = operator.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then\n# this tag can be used to specify a list of macro names that should be expanded.\n# The macro definition that is found in the sources will be used.\n# Use the PREDEFINED tag if you want to use a different macro definition.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then\n# doxygen's preprocessor will remove all function-like macros that are alone\n# on a line, have an all uppercase name, and do not end with a semicolon. Such\n# function macros are typically used for boiler-plate code, and will confuse\n# the parser if not removed.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES option can be used to specify one or more tagfiles.\n# Optionally an initial location of the external documentation\n# can be added for each tagfile. The format of a tag file without\n# this location is as follows:\n#   TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n#   TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where \"loc1\" and \"loc2\" can be relative or absolute paths or\n# URLs. If a location is present for each tag, the installdox tool\n# does not have to be run to correct the links.\n# Note that each tag file must have a unique name\n# (where the name does NOT include the path)\n# If a tag file is not located in the directory in which doxygen\n# is run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create\n# a tag file that is based on the input files it reads.\n\nGENERATE_TAGFILE       = doc/doxygen/html/Ninja.TAGFILE\n\n# If the ALLEXTERNALS tag is set to YES all external classes will be listed\n# in the class index. If set to NO only the inherited external classes\n# will be listed.\n\nALLEXTERNALS           = YES\n\n# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will\n# be listed.\n\nEXTERNAL_GROUPS        = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of `which perl').\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will\n# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base\n# or super classes. Setting the tag to NO turns the diagrams off. Note that\n# this option is superseded by the HAVE_DOT option below. This is only a\n# fallback. It is recommended to install and use dot, since it yields more\n# powerful graphs.\n\nCLASS_DIAGRAMS         = YES\n\n# If set to YES, the inheritance and collaboration graphs will hide\n# inheritance and usage relations if the target is undocumented\n# or is not a class.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz, a graph visualization\n# toolkit from AT&T and Lucent Bell Labs. The other options in this section\n# have no effect if this option is set to NO (the default)\n\nHAVE_DOT               = YES\n\n# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen\n# will generate a graph for each documented class showing the direct and\n# indirect inheritance relations. Setting this tag to YES will force the\n# the CLASS_DIAGRAMS tag to NO.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen\n# will generate a graph for each documented class showing the direct and\n# indirect implementation dependencies (inheritance, containment, and\n# class references variables) of the class with other documented classes.\n\nCOLLABORATION_GRAPH    = NO\n\n# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen\n# will generate a graph for groups, showing the direct groups dependencies\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n\nUML_LOOK               = NO\n# UML_LOOK               = YES\n\n# If set to YES, the inheritance and collaboration graphs will show the\n# relations between templates and their instances.\n\nTEMPLATE_RELATIONS     = YES\n\n# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT\n# tags are set to YES then doxygen will generate a graph for each documented\n# file showing the direct and indirect include dependencies of the file with\n# other documented files.\n\nINCLUDE_GRAPH          = YES\n\n# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and\n# HAVE_DOT tags are set to YES then doxygen will generate a graph for each\n# documented header file showing the documented files that directly or\n# indirectly include this file.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will\n# generate a call dependency graph for every global function or class method.\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command.\n\nCALL_GRAPH             = NO\n\n# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen\n# will graphical hierarchy of all classes instead of a textual one.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES\n# then doxygen will show the dependencies a directory has on other directories\n# in a graphical way. The dependency relations are determined by the #include\n# relations between the files in the directories.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. Possible values are png, jpg, or gif\n# If left blank png will be used.\n\nDOT_IMAGE_FORMAT       = png\n\n# The tag DOT_PATH can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the\n# \\dotfile command).\n\nDOTFILE_DIRS           =\n\n# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width\n# (in pixels) of the graphs generated by dot. If a graph becomes larger than\n# this value, doxygen will try to truncate the graph, so that it fits within\n# the specified constraint. Beware that most browsers cannot cope with very\n# large images.\n\n# Obsolet option.\n#MAX_DOT_GRAPH_WIDTH    = 1280\n\n# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height\n# (in pixels) of the graphs generated by dot. If a graph becomes larger than\n# this value, doxygen will try to truncate the graph, so that it fits within\n# the specified constraint. Beware that most browsers cannot cope with very\n# large images.\n\n# Obsolet option.\n#MAX_DOT_GRAPH_HEIGHT   = 1024\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the\n# graphs generated by dot. A depth value of 3 means that only nodes reachable\n# from the root by following a path via at most 3 edges will be shown. Nodes\n# that lay further from the root node will be omitted. Note that setting this\n# option to 1 or 2 may greatly reduce the computation time needed for large\n# code bases. Also note that a graph may be further truncated if the graph's\n# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH\n# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),\n# the graph is not depth-constrained.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, which results in a white background.\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10)\n# support this, this feature is disabled by default.\n# JW\n# DOT_MULTI_TARGETS      = NO\nDOT_MULTI_TARGETS      = YES\n\n# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will\n# generate a legend page explaining the meaning of the various boxes and\n# arrows in the dot generated graphs.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will\n# remove the intermediate dot files that are used to generate\n# the various graphs.\n\nDOT_CLEANUP            = YES\n\n#---------------------------------------------------------------------------\n# Configuration::additions related to the search engine\n#---------------------------------------------------------------------------\n\n# The SEARCHENGINE tag specifies whether or not a search engine should be\n# used. If set to NO the values of all tags below this one will be ignored.\n\n# JW SEARCHENGINE           = NO\nSEARCHENGINE           = YES\n"
  },
  {
    "path": "doc/manual.asciidoc",
    "content": "The Ninja build system\n======================\n\n\nIntroduction\n------------\n\nNinja is yet another build system.  It takes as input the\ninterdependencies of files (typically source code and output\nexecutables) and orchestrates building them, _quickly_.\n\nNinja joins a sea of other build systems.  Its distinguishing goal is\nto be fast.  It is born from\nhttp://neugierig.org/software/chromium/notes/2011/02/ninja.html[my\nwork on the Chromium browser project], which has over 30,000 source\nfiles and whose other build systems (including one built from custom\nnon-recursive Makefiles) would take ten seconds to start building\nafter changing one file.  Ninja is under a second.\n\nPhilosophical overview\n~~~~~~~~~~~~~~~~~~~~~~\n\nWhere other build systems are high-level languages, Ninja aims to be\nan assembler.\n\nBuild systems get slow when they need to make decisions.  When you are\nin an edit-compile cycle you want it to be as fast as possible -- you\nwant the build system to do the minimum work necessary to figure out\nwhat needs to be built immediately.\n\nNinja contains the barest functionality necessary to describe\narbitrary dependency graphs.  Its lack of syntax makes it impossible\nto express complex decisions.\n\nInstead, Ninja is intended to be used with a separate program\ngenerating its input files.  The generator program (like the\n`./configure` found in autotools projects) can analyze system\ndependencies and make as many decisions as possible up front so that\nincremental builds stay fast.  Going beyond autotools, even build-time\ndecisions like \"which compiler flags should I use?\"  or \"should I\nbuild a debug or release-mode binary?\"  belong in the `.ninja` file\ngenerator.\n\nDesign goals\n~~~~~~~~~~~~\n\nHere are the design goals of Ninja:\n\n* very fast (i.e., instant) incremental builds, even for very large\n  projects.\n\n* very little policy about how code is built.  Different projects and\n  higher-level build systems have different opinions about how code\n  should be built; for example, should built objects live alongside\n  the sources or should all build output go into a separate directory?\n  Is there a \"package\" rule that builds a distributable package of\n  the project?  Sidestep these decisions by trying to allow either to\n  be implemented, rather than choosing, even if that results in\n  more verbosity.\n\n* get dependencies correct, and in particular situations that are\n  difficult to get right with Makefiles (e.g. outputs need an implicit\n  dependency on the command line used to generate them; to build C\n  source code you need to use gcc's `-M` flags for header\n  dependencies).\n\n* when convenience and speed are in conflict, prefer speed.\n\nSome explicit _non-goals_:\n\n* convenient syntax for writing build files by hand.  _You should\n  generate your ninja files using another program_.  This is how we\n  can sidestep many policy decisions.\n\n* built-in rules. _Out of the box, Ninja has no rules for\n  e.g. compiling C code._\n\n* build-time customization of the build. _Options belong in\n  the program that generates the ninja files_.\n\n* build-time decision-making ability such as conditionals or search\n  paths. _Making decisions is slow._\n\nTo restate, Ninja is faster than other build systems because it is\npainfully simple.  You must tell Ninja exactly what to do when you\ncreate your project's `.ninja` files.\n\nComparison to Make\n~~~~~~~~~~~~~~~~~~\n\nNinja is closest in spirit and functionality to Make, relying on\nsimple dependencies between file timestamps.\n\nBut fundamentally, make has a lot of _features_: suffix rules,\nfunctions, built-in rules that e.g. search for RCS files when building\nsource.  Make's language was designed to be written by humans.  Many\nprojects find make alone adequate for their build problems.\n\nIn contrast, Ninja has almost no features; just those necessary to get\nbuilds correct while punting most complexity to generation of the\nninja input files.  Ninja by itself is unlikely to be useful for most\nprojects.\n\nHere are some of the features Ninja adds to Make.  (These sorts of\nfeatures can often be implemented using more complicated Makefiles,\nbut they are not part of make itself.)\n\n* Ninja has special support for discovering extra dependencies at build\n  time, making it easy to get <<ref_headers,header dependencies>>\n  correct for C/C++ code.\n\n* A build edge may have multiple outputs.\n\n* Outputs implicitly depend on the command line that was used to generate\n  them, which means that changing e.g. compilation flags will cause\n  the outputs to rebuild.\n\n* Output directories are always implicitly created before running the\n  command that relies on them.\n\n* Rules can provide shorter descriptions of the command being run, so\n  you can print e.g. `CC foo.o` instead of a long command line while\n  building.\n\n* Builds are always run in parallel, based by default on the number of\n  CPUs your system has.  Underspecified build dependencies will result\n  in incorrect builds.\n\n* Command output is always buffered.  This means commands running in\n  parallel don't interleave their output, and when a command fails we\n  can print its failure output next to the full command line that\n  produced the failure.\n\n\nUsing Ninja for your project\n----------------------------\n\nNinja currently works on Unix-like systems and Windows. It's seen the\nmost testing on Linux (and has the best performance there) but it runs\nfine on Mac OS X and FreeBSD.\n\nIf your project is small, Ninja's speed impact is likely unnoticeable.\n(However, even for small projects it sometimes turns out that Ninja's\nlimited syntax forces simpler build rules that result in faster\nbuilds.)  Another way to say this is that if you're happy with the\nedit-compile cycle time of your project already then Ninja won't help.\n\nThere are many other build systems that are more user-friendly or\nfeatureful than Ninja itself.  For some recommendations: the Ninja\nauthor found http://gittup.org/tup/[the tup build system] influential\nin Ninja's design, and thinks https://github.com/apenwarr/redo[redo]'s\ndesign is quite clever.\n\nNinja's benefit comes from using it in conjunction with a smarter\nmeta-build system.\n\nhttps://gn.googlesource.com/gn/[gn]:: The meta-build system used to\ngenerate build files for Google Chrome and related projects (v8,\nnode.js), as well as Google Fuchsia.  gn can generate Ninja files for\nall platforms supported by Chrome.\n\nhttps://cmake.org/[CMake]:: A widely used meta-build system that\ncan generate Ninja files on Linux as of CMake version 2.8.8.  Newer versions\nof CMake support generating Ninja files on Windows and Mac OS X too.\n\nhttps://github.com/ninja-build/ninja/wiki/List-of-generators-producing-ninja-build-files[others]:: Ninja ought to fit perfectly into other meta-build software\nlike https://premake.github.io/[premake].  If you do this work,\nplease let us know!\n\nRunning Ninja\n~~~~~~~~~~~~~\n\nRun `ninja`.  By default, it looks for a file named `build.ninja` in\nthe current directory and builds all out-of-date targets.  You can\nspecify which targets (files) to build as command line arguments.\n\nThere is also a special syntax `target^` for specifying a target\nas the first output of some rule containing the source you put in\nthe command line, if one exists. For example, if you specify target as\n`foo.c^` then `foo.o` will get built (assuming you have those targets\nin your build files).\n\n`ninja -h` prints help output.  Many of Ninja's flags intentionally\nmatch those of Make; e.g `ninja -C build -j 20` changes into the\n`build` directory and runs 20 build commands in parallel.  (Note that\nNinja defaults to running commands in parallel anyway, so typically\nyou don't need to pass `-j`.)\n\n\nGNU Jobserver support\n~~~~~~~~~~~~~~~~~~~~~\n\nSince version 1.13., Ninja builds can follow the\nhttps://www.gnu.org/software/make/manual/html_node/Job-Slots.html[GNU Make jobserver]\nclient protocol. This is useful when Ninja is invoked as part of a larger\nbuild system controlled by a top-level GNU Make instance, or any other\njobserver pool implementation, as it allows better coordination between\nconcurrent build tasks.\n\nThis feature is automatically enabled under the following conditions:\n\n- Dry-run (i.e. `-n` or `--dry-run`) is not enabled.\n\n- No explicit job count (e.g. `-j<COUNT>`) is passed on the command\n  line.\n\n- The `MAKEFLAGS` environment variable is defined and describes a valid\n  jobserver mode using `--jobserver-auth=SEMAPHORE_NAME` on Windows, or\n  `--jobserver-auth=fifo:PATH` on Posix.\n\nIn this case, Ninja will use the jobserver pool of job slots to control\nparallelism, instead of its default parallel implementation.\n\nNote that load-average limitations (i.e. when using `-l<count>`)\nare still being enforced in this mode.\n\nIMPORTANT: On Posix, only the FIFO-based version of the protocol, which is\nimplemented by GNU Make 4.4 and higher, is supported. Ninja will detect\nwhen a pipe-based jobserver is being used (i.e. when `MAKEFLAGS` contains\n`--jobserver-auth=<read>,<write>`) and will print a warning, but will\notherwise ignore it.\n\nEnvironment variables\n~~~~~~~~~~~~~~~~~~~~~\n\nNinja supports two environment variables to control its behavior:\n`NINJA_STATUS`, the progress status printed before the rule being run.\n\nSeveral placeholders are available:\n\n`%s`:: The number of started edges.\n`%t`:: The total number of edges that must be run to complete the build.\n`%p`:: The percentage of finished edges.\n`%r`:: The number of currently running edges.\n`%u`:: The number of remaining edges to start.\n`%f`:: The number of finished edges.\n`%o`:: Overall rate of finished edges per second\n`%c`:: Current rate of finished edges per second (average over builds\nspecified by `-j` or its default)\n`%e`:: Elapsed time in seconds. _(Available since Ninja 1.2.)_\n`%E`:: Remaining time (ETA) in seconds. _(Available since Ninja 1.12.)_\n`%w`:: Elapsed time in [h:]mm:ss format. _(Available since Ninja 1.12.)_\n`%W`:: Remaining time (ETA) in [h:]mm:ss format. _(Available since Ninja 1.12.)_\n`%P`:: The percentage (in ppp% format) of time elapsed out of predicted total runtime. _(Available since Ninja 1.12.)_\n`%%`:: A plain `%` character.\n\nThe default progress status is `\"[%f/%t] \"` (note the trailing space\nto separate from the build rule). Another example of possible progress status\ncould be `\"[%u/%r/%f] \"`.\n\nIf `MAKEFLAGS` is defined in the environment, if may alter how\nNinja dispatches parallel build commands. See the GNU Jobserver support\nsection for details.\n\nExtra tools\n~~~~~~~~~~~\n\nThe `-t` flag on the Ninja command line runs some tools that we have\nfound useful during Ninja's development.  The current tools are:\n\n[horizontal]\n`query`:: dump the inputs and outputs of a given target.\n\n`browse`:: browse the dependency graph in a web browser.  Clicking a\nfile focuses the view on that file, showing inputs and outputs.  This\nfeature requires a Python installation. By default, port 8000 is used\nand a web browser will be opened. This can be changed as follows:\n+\n----\nninja -t browse --port=8000 --no-browser mytarget\n----\n+\nUse `--help` to see a full list of options.\n+\n`graph`:: output a file in the syntax used by `graphviz`, an automatic\ngraph layout tool.  Use it like:\n+\n----\nninja -t graph mytarget | dot -Tpng -ograph.png\n----\n+\nIn the Ninja source tree, `ninja graph.png`\ngenerates an image for Ninja itself.  If no target is given generate a\ngraph for all root targets.\n\n`targets`:: output a list of targets either by rule or by depth.  If used\nlike +ninja -t targets rule _name_+ it prints the list of targets\nusing the given rule to be built.  If no rule is given, it prints the source\nfiles (the leaves of the graph).  If used like\n+ninja -t targets depth _digit_+ it\nprints the list of targets in a depth-first manner starting by the root\ntargets (the ones with no outputs). Indentation is used to mark dependencies.\nIf the depth is zero it prints all targets. If no arguments are provided\n+ninja -t targets depth 1+ is assumed. In this mode targets may be listed\nseveral times. If used like this +ninja -t targets all+ it\nprints all the targets available without indentation and it is faster\nthan the _depth_ mode.\n\n`commands`:: given a list of targets, print a list of commands which, if\nexecuted in order, may be used to rebuild those targets, assuming that all\noutput files are out of date. Use `-s` to only print the final command, not the chain.\n\n`inputs`:: given a list of targets, print a list of all inputs used to\nrebuild those targets. For compatibility reasons, inputs are shell-quoted by default.\nUse `--no-shell-escape` to disable quoting. See `ninja -t inputs --help` for all options.\n_Available since Ninja 1.11._\n\n`multi-inputs`:: print one or more sets of inputs required to build targets.\nEach line will consist of a target, a delimiter, an input and a terminator character.\nThe list produced by the tool can be helpful if one would like to know which targets\nthat are affected by a certain input.\n+\nThe output will be a series of lines with the following elements:\n+\n----\n<target> <delimiter> <input> <terminator>\n----\n+\nThe default `<delimiter>` is a single TAB character.\nThe delimiter can be modified to any string using the `--delimiter` argument.\n+\nThe default `<terminator>` is a line terminator (i.e. `\\n` on Posix and `\\r\\n` on Windows).\nThe terminator can be changed to `\\0` by using the `--print0` argument.\n+\n----\n----\n+\nExample usage of the `multi-inputs` tool:\n+\n----\nninja -t multi-inputs target1 target2 target3\n----\n+\nExample of produced output from the `multi-inputs` tool:\n+\n----\ntarget1 file1.c\ntarget2 file1.c\ntarget2 file2.c\ntarget3 file1.c\ntarget3 file2.c\ntarget3 file3.c\n----\n+\n_Note that a given input may appear for several targets if it is used by more\nthan one targets._\n_Available since Ninja 1.13._\n\n`clean`:: remove built files. By default, it removes all built files\nexcept for those created by the generator.  Adding the `-g` flag also\nremoves built files created by the generator (see <<ref_rule,the rule\nreference for the +generator+ attribute>>).  Additional arguments are\ntargets, which removes the given targets and recursively all files\nbuilt for them.\n+\nIf used like +ninja -t clean -r _rules_+ it removes all files built using\nthe given rules.\n+\nFiles created but not referenced in the graph are not removed. This\ntool takes in account the +-v+ and the +-n+ options (note that +-n+\nimplies +-v+).\n\n`cleandead`:: remove files produced by previous builds that are no longer in the\nbuild file. _Available since Ninja 1.10._\n\n`compdb`:: given a list of rules, each of which is expected to be a\nC family language compiler rule whose first input is the name of the\nsource file, prints on standard output a compilation database in the\nhttp://clang.llvm.org/docs/JSONCompilationDatabase.html[JSON format] expected\nby the Clang tooling interface. Use `-x` to expand `@rspfile` response file invocations.\n_Available since Ninja 1.2._\n\n`compdb-targets`:: like `compdb`, but takes a list of targets instead of rules,\nand expects at least one target. The resulting compilation database contains\nall commands required to build the indicated targets, and _only_ those\ncommands. Use `-x` to expand `@rspfile` response file invocations.\n\n`deps`:: show all dependencies stored in the `.ninja_deps` file. When given a\ntarget, show just the target's dependencies. _Available since Ninja 1.4._\n\n`missingdeps`:: given a list of targets, look for targets that depend on\na generated file, but do not have a properly (possibly transitive) dependency\non the generator.  Such targets may cause build flakiness on clean builds.\n+\nThe broken targets can be found assuming deps log / depfile dependency\ninformation is correct.  Any target that depends on a generated file (output\nof a generator-target) implicitly, but does not have an explicit or order-only\ndependency path to the generator-target, is considered broken.\n+\nThe tool's findings can be verified by trying to build the listed targets in\na clean outdir without building any other targets.  The build should fail for\neach of them with a missing include error or equivalent pointing to the\ngenerated file.\n_Available since Ninja 1.11._\n\n`recompact`:: recompact the `.ninja_deps` file. _Available since Ninja 1.4._\n\n`restat`:: updates all recorded file modification timestamps in the `.ninja_log`\nfile. _Available since Ninja 1.10._\n+\nThe build manifest won't be parsed when running this tool. If you're using a\n`builddir` you must pass it via `--builddir=DIR`. _Available since Ninja 1.14._\n\n`rules`:: output the list of all rules. It can be used to know which rule name\nto pass to +ninja -t targets rule _name_+ or +ninja -t compdb+. Adding the `-d`\nflag also prints the description of the rules.\n\n`msvc`:: Available on Windows hosts only.\nHelper tool to invoke the `cl.exe` compiler with a pre-defined set of\nenvironment variables, as in:\n+\n----\nninja -t msvc -e ENVFILE -- cl.exe <arguments>\n----\n+\nWhere `ENVFILE` is a binary file that contains an environment block suitable\nfor CreateProcessA() on Windows (i.e. a series of zero-terminated strings that\nlook like NAME=VALUE, followed by an extra zero terminator). Note that this uses\nthe local codepage encoding.\n+\nThis tool also supports a deprecated way of parsing the compiler's output when\nthe `/showIncludes` flag is used, and generating a GCC-compatible depfile from it:\n+\n----\nninja -t msvc -o DEPFILE [-p STRING] -- cl.exe /showIncludes <arguments>\n----\n+\nWhen using this option, `-p STRING` can be used to pass the localized line prefix\nthat `cl.exe` uses to output dependency information. For English-speaking regions\nthis is `\"Note: including file: \"` without the double quotes, but will be different\nfor other regions.\n+\nNote that Ninja supports this natively now, with the use of `deps = msvc` and\n`msvc_deps_prefix` in Ninja files. Native support also avoids launching an extra\ntool process each time the compiler must be called, which can speed up builds\nnoticeably on Windows.\n\n`wincodepage`:: Available on Windows hosts (_since Ninja 1.11_).\nPrints the Windows code page whose encoding is expected in the build file.\nThe output has the form:\n+\n----\nBuild file encoding: <codepage>\n----\n+\nAdditional lines may be added in future versions of Ninja.\n+\nThe `<codepage>` is one of:\n\n`UTF-8`::: Encode as UTF-8.\n\n`ANSI`::: Encode to the system-wide ANSI code page.\n\nWriting your own Ninja files\n----------------------------\n\nThe remainder of this manual is only useful if you are constructing\nNinja files yourself: for example, if you're writing a meta-build\nsystem or supporting a new language.\n\nConceptual overview\n~~~~~~~~~~~~~~~~~~~\n\nNinja evaluates a graph of dependencies between files, and runs\nwhichever commands are necessary to make your build target up to date\nas determined by file modification times.  If you are familiar with\nMake, Ninja is very similar.\n\nA build file (default name: `build.ninja`) provides a list of _rules_\n-- short names for longer commands, like how to run the compiler --\nalong with a list of _build_ statements saying how to build files\nusing the rules -- which rule to apply to which inputs to produce\nwhich outputs.\n\nConceptually, `build` statements describe the dependency graph of your\nproject, while `rule` statements describe how to generate the files\nalong a given edge of the graph.\n\nSyntax example\n~~~~~~~~~~~~~~\n\nHere's a basic `.ninja` file that demonstrates most of the syntax.\nIt will be used as an example for the following sections.\n\n---------------------------------\ncflags = -Wall\n\nrule cc\n  command = gcc $cflags -c $in -o $out\n\nbuild foo.o: cc foo.c\n---------------------------------\n\nVariables\n~~~~~~~~~\nDespite the non-goal of being convenient to write by hand, to keep\nbuild files readable (debuggable), Ninja supports declaring shorter\nreusable names for strings.  A declaration like the following\n\n----------------\ncflags = -g\n----------------\n\ncan be used on the right side of an equals sign, dereferencing it with\na dollar sign, like this:\n\n----------------\nrule cc\n  command = gcc $cflags -c $in -o $out\n----------------\n\nVariables can also be referenced using curly braces like `${in}`.\n\nVariables might better be called \"bindings\", in that a given variable\ncannot be changed, only shadowed.  There is more on how shadowing works\nlater in this document.\n\nRules\n~~~~~\n\nRules declare a short name for a command line.  They begin with a line\nconsisting of the `rule` keyword and a name for the rule.  Then\nfollows an indented set of `variable = value` lines.\n\nThe basic example above declares a new rule named `cc`, along with the\ncommand to run.  In the context of a rule, the `command` variable\ndefines the command to run, `$in` expands to the list of\ninput files (`foo.c`), and `$out` to the output files (`foo.o`) for the\ncommand.  A full list of special variables is provided in\n<<ref_rule,the reference>>.\n\nBuild statements\n~~~~~~~~~~~~~~~~\n\nBuild statements declare a relationship between input and output\nfiles.  They begin with the `build` keyword, and have the format\n+build _outputs_: _rulename_ _inputs_+.  Such a declaration says that\nall of the output files are derived from the input files.  When the\noutput files are missing or when the inputs change, Ninja will run the\nrule to regenerate the outputs.\n\nThe basic example above describes how to build `foo.o`, using the `cc`\nrule.\n\nIn the scope of a `build` block (including in the evaluation of its\nassociated `rule`), the variable `$in` is the list of inputs and the\nvariable `$out` is the list of outputs.\n\nA build statement may be followed by an indented set of `key = value`\npairs, much like a rule.  These variables will shadow any variables\nwhen evaluating the variables in the command.  For example:\n\n----------------\ncflags = -Wall -Werror\nrule cc\n  command = gcc $cflags -c $in -o $out\n\n# If left unspecified, builds get the outer $cflags.\nbuild foo.o: cc foo.c\n\n# But you can shadow variables like cflags for a particular build.\nbuild special.o: cc special.c\n  cflags = -Wall\n\n# The variable was only shadowed for the scope of special.o;\n# Subsequent build lines get the outer (original) cflags.\nbuild bar.o: cc bar.c\n\n----------------\n\nFor more discussion of how scoping works, consult <<ref_scope,the\nreference>>.\n\nIf you need more complicated information passed from the build\nstatement to the rule (for example, if the rule needs \"the file\nextension of the first input\"), pass that through as an extra\nvariable, like how `cflags` is passed above.\n\nIf the top-level Ninja file is specified as an output of any build\nstatement and it is out of date, Ninja will rebuild and reload it\nbefore building the targets requested by the user.\n\nGenerating Ninja files from code\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`misc/ninja_syntax.py` in the Ninja distribution is a tiny Python\nmodule to facilitate generating Ninja files.  It allows you to make\nPython calls like `ninja.rule(name='foo', command='bar',\ndepfile='$out.d')` and it will generate the appropriate syntax.  Feel\nfree to just inline it into your project's build system if it's\nuseful.\n\n\nMore details\n------------\n\nThe `phony` rule\n~~~~~~~~~~~~~~~~\n\nThe special rule name `phony` can be used to create aliases for other\ntargets.  For example:\n\n----------------\nbuild foo: phony some/file/in/a/faraway/subdir/foo\n----------------\n\nThis makes `ninja foo` build the longer path.  Semantically, the\n`phony` rule is equivalent to a plain rule where the `command` does\nnothing, but phony rules are handled specially in that they aren't\nprinted when run, logged (see below), nor do they contribute to the\ncommand count printed as part of the build process.\n\nWhen a `phony` target is used as an input to another build rule, the\nother build rule will, semantically, consider the inputs of the\n`phony` rule as its own. Therefore, `phony` rules can be used to group\ninputs, e.g. header files.\n\n`phony` can also be used to create dummy targets for files which\nmay not exist at build time.  If a phony build statement is written\nwithout any dependencies, the target will be considered out of date if\nit does not exist.  Without a phony build statement, Ninja will report\nan error if the file does not exist and is required by the build.\n\nTo create a rule that never rebuilds, use a build rule without any input:\n----------------\nrule touch\n  command = touch $out\nbuild file_that_always_exists.dummy: touch\nbuild dummy_target_to_follow_a_pattern: phony file_that_always_exists.dummy\n----------------\n\n\nDefault target statements\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default, if no targets are specified on the command line, Ninja\nwill build every output that is not named as an input elsewhere.\nYou can override this behavior using a default target statement.\nA default target statement causes Ninja to build only a given subset\nof output files if none are specified on the command line.\n\nDefault target statements begin with the `default` keyword, and have\nthe format +default _targets_+.  A default target statement must appear\nafter the build statement that declares the target as an output file.\nThey are cumulative, so multiple statements may be used to extend\nthe list of default targets.  For example:\n\n----------------\ndefault foo bar\ndefault baz\n----------------\n\nThis causes Ninja to build the `foo`, `bar` and `baz` targets by\ndefault.\n\n\n[[ref_log]]\nThe Ninja log\n~~~~~~~~~~~~~\n\nFor each built file, Ninja keeps a log of the command used to build\nit.  Using this log Ninja can know when an existing output was built\nwith a different command line than the build files specify (i.e., the\ncommand line changed) and knows to rebuild the file.\n\nThe log file is kept in the build root in a file called `.ninja_log`.\nIf you provide a variable named `builddir` in the outermost scope,\n`.ninja_log` will be kept in that directory instead.\n\n\n[[ref_versioning]]\nVersion compatibility\n~~~~~~~~~~~~~~~~~~~~~\n\n_Available since Ninja 1.2._\n\nNinja version labels follow the standard major.minor.patch format,\nwhere the major version is increased on backwards-incompatible\nsyntax/behavioral changes and the minor version is increased on new\nbehaviors.  Your `build.ninja` may declare a variable named\n`ninja_required_version` that asserts the minimum Ninja version\nrequired to use the generated file.  For example,\n\n-----\nninja_required_version = 1.1\n-----\n\ndeclares that the build file relies on some feature that was\nintroduced in Ninja 1.1 (perhaps the `pool` syntax), and that\nNinja 1.1 or greater must be used to build.  Unlike other Ninja\nvariables, this version requirement is checked immediately when\nthe variable is encountered in parsing, so it's best to put it\nat the top of the build file.\n\nNinja always warns if the major versions of Ninja and the\n`ninja_required_version` don't match; a major version change hasn't\ncome up yet so it's difficult to predict what behavior might be\nrequired.\n\n[[ref_headers]]\nC/C++ header dependencies\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo get C/C++ header dependencies (or any other build dependency that\nworks in a similar way) correct Ninja has some extra functionality.\n\nThe problem with headers is that the full list of files that a given\nsource file depends on can only be discovered by the compiler:\ndifferent preprocessor defines and include paths cause different files\nto be used.  Some compilers can emit this information while building,\nand Ninja can use that to get its dependencies perfect.\n\nConsider: if the file has never been compiled, it must be built anyway,\ngenerating the header dependencies as a side effect.  If any file is\nlater modified (even in a way that changes which headers it depends\non) the modification will cause a rebuild as well, keeping the\ndependencies up to date.\n\nWhen loading these special dependencies, Ninja implicitly adds extra\nbuild edges such that it is not an error if the listed dependency is\nmissing.  This allows you to delete a header file and rebuild without\nthe build aborting due to a missing input.\n\ndepfile\n^^^^^^^\n\n`gcc` (and other compilers like `clang`) support emitting dependency\ninformation in the syntax of a Makefile.  (Any command that can write\ndependencies in this form can be used, not just `gcc`.)\n\nTo bring this information into Ninja requires cooperation.  On the\nNinja side, the `depfile` attribute on the `build` must point to a\npath where this data is written.  (Ninja only supports the limited\nsubset of the Makefile syntax emitted by compilers.)  Then the command\nmust know to write dependencies into the `depfile` path.\nUse it like in the following example:\n\n----\nrule cc\n  depfile = $out.d\n  command = gcc -MD -MF $out.d [other gcc flags here]\n----\n\nThe `-MD` flag to `gcc` tells it to output header dependencies, and\nthe `-MF` flag tells it where to write them.\n\ndeps\n^^^^\n\n_(Available since Ninja 1.3.)_\n\nIt turns out that for large projects (and particularly on Windows,\nwhere the file system is slow) loading these dependency files on\nstartup is slow.\n\nNinja 1.3 can instead process dependencies just after they're generated\nand save a compacted form of the same information in a Ninja-internal\ndatabase.\n\nNinja supports this processing in two forms.\n\n1. `deps = gcc` specifies that the tool outputs `gcc`-style dependencies\n   in the form of Makefiles.  Adding this to the above example will\n   cause Ninja to process the `depfile` immediately after the\n   compilation finishes, then delete the `.d` file (which is only used\n   as a temporary).\n\n2. `deps = msvc` specifies that the tool outputs header dependencies\n   in the form produced by the Visual Studio compiler's\n   http://msdn.microsoft.com/en-us/library/hdkef6tk(v=vs.90).aspx[`/showIncludes`\n   flag].  Briefly, this means the tool outputs specially-formatted lines\n   to its stdout.  Ninja then filters these lines from the displayed\n   output.  No `depfile` attribute is necessary, but the localized string\n   in front of the header file path should be globally defined. For instance,\n   `msvc_deps_prefix = Note: including file:`\n   for an English Visual Studio (the default).\n+\n----\nmsvc_deps_prefix = Note: including file:\nrule cc\n  deps = msvc\n  command = cl /showIncludes -c $in /Fo$out\n----\n\nIf the include directory directives are using absolute paths, your depfile\nmay result in a mixture of relative and absolute paths. Paths used by other\nbuild rules need to match exactly. Therefore, it is recommended to use\nrelative paths in these cases.\n\n[[ref_pool]]\nPools\n~~~~~\n\n_Available since Ninja 1.1._\n\nPools allow you to allocate one or more rules or edges a finite number\nof concurrent jobs which is more tightly restricted than the default\nparallelism.\n\nThis can be useful, for example, to restrict a particular expensive rule\n(like link steps for huge executables), or to restrict particular build\nstatements which you know perform poorly when run concurrently.\n\nEach pool has a `depth` variable which is specified in the build file.\nThe pool is then referred to with the `pool` variable on either a rule\nor a build statement.\n\nNo matter what pools you specify, ninja will never run more concurrent jobs\nthan the default parallelism, or the number of jobs specified on the command\nline (with `-j`).\n\n----------------\n# No more than 4 links at a time.\npool link_pool\n  depth = 4\n\n# No more than 1 heavy object at a time.\npool heavy_object_pool\n  depth = 1\n\nrule link\n  ...\n  pool = link_pool\n\nrule cc\n  ...\n\n# The link_pool is used here. Only 4 links will run concurrently.\nbuild foo.exe: link input.obj\n\n# A build statement can be exempted from its rule's pool by setting an\n# empty pool. This effectively puts the build statement back into the default\n# pool, which has infinite depth.\nbuild other.exe: link input.obj\n  pool =\n\n# A build statement can specify a pool directly.\n# Only one of these builds will run at a time.\nbuild heavy_object1.obj: cc heavy_obj1.cc\n  pool = heavy_object_pool\nbuild heavy_object2.obj: cc heavy_obj2.cc\n  pool = heavy_object_pool\n\n----------------\n\nThe `console` pool\n^^^^^^^^^^^^^^^^^^\n\n_Available since Ninja 1.5._\n\nThere exists a pre-defined pool named `console` with a depth of 1. It has\nthe special property that any task in the pool has direct access to the\nstandard input, output and error streams provided to Ninja, which are\nnormally connected to the user's console (hence the name) but could be\nredirected. This can be useful for interactive tasks or long-running tasks\nwhich produce status updates on the console (such as test suites).\n\nWhile a task in the `console` pool is running, Ninja's regular output (such\nas progress status and output from concurrent tasks) is buffered until\nit completes.\n\n[[ref_ninja_file]]\nNinja file reference\n--------------------\n\nA file is a series of declarations.  A declaration can be one of:\n\n1. A rule declaration, which begins with +rule _rulename_+, and\n   then has a series of indented lines defining variables.\n\n2. A build edge, which looks like +build _output1_ _output2_:\n   _rulename_ _input1_ _input2_+. +\n   Implicit dependencies may be tacked on the end with +|\n   _dependency1_ _dependency2_+. +\n   Order-only dependencies may be tacked on the end with +||\n   _dependency1_ _dependency2_+.  (See <<ref_dependencies,the reference on\n   dependency types>>.)\n   Validations may be tacked on the end with +|@ _validation1_ _validation2_+.\n   (See <<validations,the reference on validations>>.)\n+\nImplicit outputs _(available since Ninja 1.7)_ may be added before\nthe `:` with +| _output1_ _output2_+ and do not appear in `$out`.\n(See <<ref_outputs,the reference on output types>>.)\n\n3. Variable declarations, which look like +_variable_ = _value_+.\n\n4. Default target statements, which look like +default _target1_ _target2_+.\n\n5. References to more files, which look like +subninja _path_+ or\n   +include _path_+.  The difference between these is explained below\n   <<ref_scope,in the discussion about scoping>>.\n\n6. A pool declaration, which looks like +pool _poolname_+. Pools are explained\n   <<ref_pool, in the section on pools>>.\n\n[[ref_lexer]]\nLexical syntax\n~~~~~~~~~~~~~~\n\nNinja is mostly encoding agnostic, as long as the bytes Ninja cares\nabout (like slashes in paths) are ASCII.  This means e.g. UTF-8 or\nISO-8859-1 input files ought to work.\n\nComments begin with `#` and extend to the end of the line.\n\nNewlines are significant.  Statements like `build foo bar` are a set\nof space-separated tokens that end at the newline.  Newlines and\nspaces within a token must be escaped.\n\nThere is only one escape character, `$`, and it has the following\nbehaviors:\n\n`$` followed by a newline:: escape the newline (continue the current line\nacross a line break).\n\n`$` followed by text:: a variable reference.\n\n`${varname}`:: alternate syntax for `$varname`.\n\n`$` followed by space:: a space.  (This is only necessary in lists of\npaths, where a space would otherwise separate filenames.  See below.)\n\n`$:` :: a colon.  (This is only necessary in `build` lines, where a colon\nwould otherwise terminate the list of outputs.)\n\n`$^` :: a newline _(available since Ninja 1.14)_.\n\nInserts '\\n' into the resulting string. This is useful to\nwrite build commands that require several shell lines as a single `command`\nvalue in the build plan.\n\nRequires `ninja_required_version` to be specified and greater or equal to 1.14\nin the build file.\n\n`$$`:: a literal `$`.\n\nA `build` or `default` statement is first parsed as a space-separated\nlist of filenames and then each name is expanded.  This means that\nspaces within a variable will result in spaces in the expanded\nfilename.\n\n----\nspaced = foo bar\nbuild $spaced/baz other$ file: ...\n# The above build line has two outputs: \"foo bar/baz\" and \"other file\".\n----\n\nIn a `name = value` statement, whitespace at the beginning of a value\nis always stripped.  Whitespace at the beginning of a line after a\nline continuation is also stripped.\n\n----\ntwo_words_with_one_space = foo $\n    bar\none_word_with_no_space = foo$\n    bar\n----\n\nOther whitespace is only significant if it's at the beginning of a\nline.  If a line is indented more than the previous one, it's\nconsidered part of its parent's scope; if it is indented less than the\nprevious one, it closes the previous scope.\n\n[[ref_toplevel]]\nTop-level variables\n~~~~~~~~~~~~~~~~~~~\n\nTwo variables are significant when declared in the outermost file scope.\n\n`builddir`:: a directory for some Ninja output files.  See <<ref_log,the\n  discussion of the build log>>.  (You can also store other build output\n  in this directory.)\n\n`ninja_required_version`:: the minimum version of Ninja required to process\n  the build correctly.  See <<ref_versioning,the discussion of versioning>>.\n\n\n[[ref_rule]]\nRule variables\n~~~~~~~~~~~~~~\n\nA `rule` block contains a list of `key = value` declarations that\naffect the processing of the rule.  Here is a full list of special\nkeys.\n\n`command` (_required_):: the command line to run.  Each `rule` may\n  have only one `command` declaration. See <<ref_rule_command,the next\n  section>> for more details on quoting and executing multiple commands.\n\n`depfile`:: path to an optional `Makefile` that contains extra\n  _implicit dependencies_ (see <<ref_dependencies,the reference on\n  dependency types>>).  This is explicitly to support C/C++ header\n  dependencies; see <<ref_headers,the full discussion>>.\n\n`deps`:: _(Available since Ninja 1.3.)_ if present, must be one of\n  `gcc` or `msvc` to specify special dependency processing.  See\n   <<ref_headers,the full discussion>>.  The generated database is\n   stored as `.ninja_deps` in the `builddir`, see <<ref_toplevel,the\n   discussion of `builddir`>>.\n\n`msvc_deps_prefix`:: _(Available since Ninja 1.5.)_ defines the string\n  which should be stripped from msvc's /showIncludes output. Only\n  needed when `deps = msvc` and no English Visual Studio version is used.\n\n`description`:: a short description of the command, used to pretty-print\n  the command as it's running.  The `-v` flag controls whether to print\n  the full command or its description; if a command fails, the full command\n  line will always be printed before the command's output.\n\n`dyndep`:: _(Available since Ninja 1.10.)_ Used only on build statements.\n  If present, must name one of the build statement inputs.  Dynamically\n  discovered dependency information will be loaded from the file.\n  See the <<ref_dyndep,dynamic dependencies>> section for details.\n\n`generator`:: if present, specifies that this rule is used to\n  re-invoke the generator program.  Files built using `generator`\n  rules are treated specially in two ways: firstly, they will not be\n  rebuilt if the command line changes; and secondly, they are not\n  cleaned by default.\n\n`in`:: the space-separated list of files provided as inputs to the build line\n  referencing this `rule`, shell-quoted if it appears in commands.  (`$in` is\n  provided solely for convenience; if you need some subset or variant of this\n  list of files, just construct a new variable with that list and use\n  that instead.)\n\n`in_newline`:: the same as `$in` except that multiple inputs are\n  separated by newlines rather than spaces.  (For use with\n  `$rspfile_content`; this works around a bug in the MSVC linker where\n  it uses a fixed-size buffer for processing input.)\n\n`out`:: the space-separated list of files provided as outputs to the build line\n  referencing this `rule`, shell-quoted if it appears in commands.\n\n`restat`:: if present, causes Ninja to re-stat the command's outputs\n  after execution of the command.  Each output whose modification time\n  the command did not change will be treated as though it had never\n  needed to be built.  This may cause the output's reverse\n  dependencies to be removed from the list of pending build actions.\n\n`rspfile`, `rspfile_content`:: if present (both), Ninja will use a\n  response file for the given command, i.e. write the selected string\n  (`rspfile_content`) to the given file (`rspfile`) before calling the\n  command and delete the file after successful execution of the\n  command.\n+\nThis is particularly useful on Windows OS, where the maximal length of\na command line is limited and response files must be used instead.\n+\nUse it like in the following example:\n+\n----\nrule link\n  command = link.exe /OUT$out [usual link flags here] @$out.rsp\n  rspfile = $out.rsp\n  rspfile_content = $in\n\nbuild myapp.exe: link a.obj b.obj [possibly many other .obj files]\n----\n\n[[ref_rule_command]]\nInterpretation of the `command` variable\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nFundamentally, command lines behave differently on Unixes and Windows.\n\nOn Unixes, commands are arrays of arguments.  The Ninja `command`\nvariable is passed directly to `sh -c`, which is then responsible for\ninterpreting that string into an argv array.  Therefore, the quoting\nrules are those of the shell, and you can use all the normal shell\noperators, like `&&` to chain multiple commands, or `VAR=value cmd` to\nset environment variables.\n\nOn Windows, commands are strings, so Ninja passes the `command` string\ndirectly to `CreateProcess`.  (In the common case of simply executing\na compiler this means there is less overhead.)  Consequently, the\nquoting rules are determined by the called program, which on Windows\nare usually provided by the C library.  If you need shell\ninterpretation of the command (such as the use of `&&` to chain\nmultiple commands), make the command execute the Windows shell by\nprefixing the command with `cmd /c`. Ninja may error with \"invalid parameter\"\nwhich usually indicates that the command line length has been exceeded.\n\n[[ref_outputs]]\nBuild outputs\n~~~~~~~~~~~~~\n\nThere are two types of build outputs which are subtly different.\n\n1. _Explicit outputs_, as listed in a build line.  These are\n   available as the `$out` variable in the rule.\n+\nThis is the standard form of output to be used for e.g. the\nobject file of a compile command.\n\n2. _Implicit outputs_, as listed in a build line with the syntax +|\n   _out1_ _out2_+ before the `:` of a build line _(available since\n   Ninja 1.7)_.  The semantics are identical to explicit outputs,\n  the only difference is that implicit outputs don't show up in the\n  `$out` variable.\n+\nThis is for expressing outputs that don't show up on the\ncommand line of the command.\n\n[[ref_dependencies]]\nBuild dependencies\n~~~~~~~~~~~~~~~~~~\n\nThere are three types of build dependencies which are subtly different.\n\n1. _Explicit dependencies_, as listed in a build line.  These are\n   available as the `$in` variable in the rule.  Changes in these files\n   cause the output to be rebuilt; if these files are missing and\n   Ninja doesn't know how to build them, the build is aborted.\n+\nThis is the standard form of dependency to be used e.g. for the\nsource file of a compile command.\n\n2. _Implicit dependencies_, either as picked up from\n   a `depfile` attribute on a rule or from the syntax +| _dep1_\n   _dep2_+ on the end of a build line.  The semantics are identical to\n   explicit dependencies, the only difference is that implicit dependencies\n   don't show up in the `$in` variable.\n+\nThis is for expressing dependencies that don't show up on the\ncommand line of the command; for example, for a rule that runs a\nscript that reads a hardcoded file, the hardcoded file should\nbe an implicit dependency, as changes to the file should cause\nthe output to rebuild, even though it doesn't show up in the arguments.\n+\nNote that dependencies as loaded through depfiles have slightly different\nsemantics, as described in the <<ref_rule,rule reference>>.\n\n3. _Order-only dependencies_, expressed with the syntax +|| _dep1_\n   _dep2_+ on the end of a build line.  When these are out of date, the\n   output is not rebuilt until they are built, but changes in order-only\n   dependencies alone do not cause the output to be rebuilt.\n+\nOrder-only dependencies can be useful for bootstrapping dependencies\nthat are only discovered during build time: for example, to generate a\nheader file before starting a subsequent compilation step.  (Once the\nheader is used in compilation, a generated dependency file will then\nexpress the implicit dependency.)\n\nFile paths are compared as is, which means that an absolute path and a\nrelative path, pointing to the same file, are considered different by Ninja.\n\n[[validations]]\nValidations\n~~~~~~~~~~~\n\n_Available since Ninja 1.11._\n\nValidations listed on the build line cause the specified files to be\nadded to the top level of the build graph (as if they were specified\non the Ninja command line) whenever the build line is a transitive\ndependency of one of the targets specified on the command line or a\ndefault target.\n\nValidations are added to the build graph regardless of whether the output\nfiles of the build statement are dirty are not, and the dirty state of\nthe build statement that outputs the file being used as a validation\nhas no effect on the dirty state of the build statement that requested it.\n\nA build edge can list another build edge as a validation even if the second\nedge depends on the first.\n\nValidations are designed to handle rules that perform error checking but\ndon't produce any artifacts needed by the build, for example, static\nanalysis tools.  Marking the static analysis rule as an implicit input\nof the main build rule of the source files or of the rules that depend\non the main build rule would slow down the critical path of the build,\nbut using a validation would allow the build to proceed in parallel with\nthe static analysis rule once the main build rule is complete.\n\nVariable expansion\n~~~~~~~~~~~~~~~~~~\n\nVariables are expanded in paths (in a `build` or `default` statement)\nand on the right side of a `name = value` statement.\n\nWhen a `name = value` statement is evaluated, its right-hand side is\nexpanded immediately (according to the below scoping rules), and\nfrom then on `$name` expands to the static string as the result of the\nexpansion.  It is never the case that you'll need to \"double-escape\" a\nvalue to prevent it from getting expanded twice.\n\nAll variables are expanded immediately as they're encountered in parsing,\nwith one important exception: variables in `rule` blocks are expanded\nwhen the rule is _used_, not when it is declared.  In the following\nexample, the `demo` rule prints \"this is a demo of bar\".\n\n----\nrule demo\n  command = echo \"this is a demo of $foo\"\n\nbuild out: demo\n  foo = bar\n----\n\n[[ref_scope]]\nEvaluation and scoping\n~~~~~~~~~~~~~~~~~~~~~~\n\nTop-level variable declarations are scoped to the file they occur in.\n\nRule declarations are also scoped to the file they occur in.\n_(Available since Ninja 1.6)_\n\nThe `subninja` keyword, used to include another `.ninja` file,\nintroduces a new scope.  The included `subninja` file may use the\nvariables and rules from the parent file, and shadow their values for the file's\nscope, but it won't affect values of the variables in the parent.\n\nTo include another `.ninja` file in the current scope, much like a C\n`#include` statement, use `include` instead of `subninja`.\n\nVariable declarations indented in a `build` block are scoped to the\n`build` block.  The full lookup order for a variable expanded in a\n`build` block (or the `rule` is uses) is:\n\n1. Special built-in variables (`$in`, `$out`).\n\n2. Build-level variables from the `build` block.\n\n3. Rule-level variables from the `rule` block (i.e. `$command`).\n   (Note from the above discussion on expansion that these are\n   expanded \"late\", and may make use of in-scope bindings like `$in`.)\n\n4. File-level variables from the file that the `build` line was in.\n\n5. Variables from the file that included that file using the\n   `subninja` keyword.\n\n[[ref_dyndep]]\nDynamic Dependencies\n--------------------\n\n_Available since Ninja 1.10._\n\nSome use cases require implicit dependency information to be dynamically\ndiscovered from source file content _during the build_ in order to build\ncorrectly on the first run (e.g. Fortran module dependencies).  This is\nunlike <<ref_headers,header dependencies>> which are only needed on the\nsecond run and later to rebuild correctly.  A build statement may have a\n`dyndep` binding naming one of its inputs to specify that dynamic\ndependency information must be loaded from the file.  For example:\n\n----\nbuild out: ... || foo\n  dyndep = foo\nbuild foo: ...\n----\n\nThis specifies that file `foo` is a dyndep file.  Since it is an input,\nthe build statement for `out` can never be executed before `foo` is built.\nAs soon as `foo` is finished Ninja will read it to load dynamically\ndiscovered dependency information for `out`.  This may include additional\nimplicit inputs and/or outputs.  Ninja will update the build graph\naccordingly and the build will proceed as if the information was known\noriginally.\n\nDyndep file reference\n~~~~~~~~~~~~~~~~~~~~~\n\nFiles specified by `dyndep` bindings use the same <<ref_lexer,lexical syntax>>\nas <<ref_ninja_file,ninja build files>> and have the following layout.\n\n1. A version number in the form `<major>[.<minor>][<suffix>]`:\n+\n----\nninja_dyndep_version = 1\n----\n+\nCurrently the version number must always be `1` or `1.0` but may have\nan arbitrary suffix.\n\n2. One or more build statements of the form:\n+\n----\nbuild out | imp-outs... : dyndep | imp-ins...\n----\n+\nEvery statement must specify exactly one explicit output and must use\nthe rule name `dyndep`.  The `| imp-outs...` and `| imp-ins...` portions\nare optional.\n\n3. An optional `restat` <<ref_rule,variable binding>> on each build statement.\n\nThe build statements in a dyndep file must have a one-to-one correspondence\nto build statements in the <<ref_ninja_file,ninja build file>> that name the\ndyndep file in a `dyndep` binding.  No dyndep build statement may be omitted\nand no extra build statements may be specified.\n\nDyndep Examples\n~~~~~~~~~~~~~~~\n\nFortran Modules\n^^^^^^^^^^^^^^^\n\nConsider a Fortran source file `foo.f90` that provides a module\n`foo.mod` (an implicit output of compilation) and another source file\n`bar.f90` that uses the module (an implicit input of compilation).  This\nimplicit dependency must be discovered before we compile either source\nin order to ensure that `bar.f90` never compiles before `foo.f90`, and\nthat `bar.f90` recompiles when `foo.mod` changes.  We can achieve this\nas follows:\n\n----\nrule f95\n  command = f95 -o $out -c $in\nrule fscan\n  command = fscan -o $out $in\n\nbuild foobar.dd: fscan foo.f90 bar.f90\n\nbuild foo.o: f95 foo.f90 || foobar.dd\n  dyndep = foobar.dd\nbuild bar.o: f95 bar.f90 || foobar.dd\n  dyndep = foobar.dd\n----\n\nIn this example the order-only dependencies ensure that `foobar.dd` is\ngenerated before either source compiles.  The hypothetical `fscan` tool\nscans the source files, assumes each will be compiled to a `.o` of the\nsame name, and writes `foobar.dd` with content such as:\n\n----\nninja_dyndep_version = 1\nbuild foo.o | foo.mod: dyndep\nbuild bar.o: dyndep |  foo.mod\n----\n\nNinja will load this file to add `foo.mod` as an implicit output of\n`foo.o` and implicit input of `bar.o`.  This ensures that the Fortran\nsources are always compiled in the proper order and recompiled when\nneeded.\n\nTarball Extraction\n^^^^^^^^^^^^^^^^^^\n\nConsider a tarball `foo.tar` that we want to extract.  The extraction time\ncan be recorded with a `foo.tar.stamp` file so that extraction repeats if\nthe tarball changes, but we also would like to re-extract if any of the\noutputs is missing.  However, the list of outputs depends on the content\nof the tarball and cannot be spelled out explicitly in the ninja build file.\nWe can achieve this as follows:\n\n----\nrule untar\n  command = tar xf $in && touch $out\nrule scantar\n  command = scantar --stamp=$stamp --dd=$out $in\nbuild foo.tar.dd: scantar foo.tar\n  stamp = foo.tar.stamp\nbuild foo.tar.stamp: untar foo.tar || foo.tar.dd\n  dyndep = foo.tar.dd\n----\n\nIn this example the order-only dependency ensures that `foo.tar.dd` is\nbuilt before the tarball extracts.  The hypothetical `scantar` tool\nwill read the tarball (e.g. via `tar tf`) and write `foo.tar.dd` with\ncontent such as:\n\n----\nninja_dyndep_version = 1\nbuild foo.tar.stamp | file1.txt file2.txt : dyndep\n  restat = 1\n----\n\nNinja will load this file to add `file1.txt` and `file2.txt` as implicit\noutputs of `foo.tar.stamp`, and to mark the build statement for `restat`.\nOn future builds, if any implicit output is missing the tarball will be\nextracted again.  The `restat` binding tells Ninja to tolerate the fact\nthat the implicit outputs may not have modification times newer than\nthe tarball itself (avoiding re-extraction on every build).\n"
  },
  {
    "path": "doc/style.css",
    "content": ":root {\n    color-scheme: light dark;\n}\n\nbody {\n    margin: 5ex 10ex;\n    max-width: 80ex;\n    line-height: 1.5;\n    font-family: sans-serif;\n}\n\nh1, h2, h3 {\n    font-weight: normal;\n}\n\npre, code {\n    font-family: x, monospace;\n}\n\npre {\n    padding: 1ex;\n    background: #eee;\n    border: solid 1px #ddd;\n    min-width: 0;\n    font-size: 90%;\n}\n@media (prefers-color-scheme: dark) {\n  pre {\n    background: #333;\n    border: solid 1px #444;\n  }\n}\n\ncode {\n    color: #007;\n}\n@media (prefers-color-scheme: dark) {\n  code {\n    color: #a7cec8;\n  }\n}\n\ndiv.chapter {\n    margin-top: 4em;\n    border-top: solid 2px black;\n}\n@media (prefers-color-scheme: dark) {\n  div.chapter {\n    border-top: solid 2px white;\n  }\n}\n\np {\n    margin-top: 0;\n}\n\n/* The following applies to the left column of a [horizontal] labeled list: */\ntable.horizontal > tbody > tr > td:nth-child(1) {\n\n    /* prevent the insertion of a line-break in the middle of a label: */\n    white-space: nowrap;\n\n    /* insert a little horizontal padding between the two columns: */\n    padding-right: 1.5em;\n\n    /* right-justify labels: */\n    text-align: end;\n}\n"
  },
  {
    "path": "misc/afl-fuzz/build.ninja",
    "content": "rule b\n  command = clang -MMD -MF $out.d -o $out -c $in\n  description = building $out\n\nbuild a.o: b a.c\n"
  },
  {
    "path": "misc/afl-fuzz-tokens/kw_build",
    "content": "build"
  },
  {
    "path": "misc/afl-fuzz-tokens/kw_default",
    "content": "default"
  },
  {
    "path": "misc/afl-fuzz-tokens/kw_include",
    "content": "include"
  },
  {
    "path": "misc/afl-fuzz-tokens/kw_pool",
    "content": "pool"
  },
  {
    "path": "misc/afl-fuzz-tokens/kw_rule",
    "content": "rule"
  },
  {
    "path": "misc/afl-fuzz-tokens/kw_subninja",
    "content": "subninja"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_a",
    "content": "a"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_b",
    "content": "b"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_colon",
    "content": ":"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_cont",
    "content": "$\n"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_dollar",
    "content": "$"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_eq",
    "content": "="
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_indent",
    "content": "  "
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_pipe",
    "content": "|"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_pipepipe",
    "content": "||"
  },
  {
    "path": "misc/afl-fuzz-tokens/misc_space",
    "content": " "
  },
  {
    "path": "misc/bash-completion",
    "content": "# Copyright 2011 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Add the following to your .bashrc to tab-complete ninja targets\n#   . path/to/ninja/misc/bash-completion\n\n_ninja_target() {\n    local cur prev targets dir line targets_command OPTIND\n\n    # When available, use bash_completion to:\n    #   1) Complete words when the cursor is in the middle of the word\n    #   2) Complete paths with files or directories, as appropriate\n    if _get_comp_words_by_ref cur prev &>/dev/null ; then\n        case $prev in\n            -f)\n                _filedir\n                return 0\n                ;;\n            -C)\n                _filedir -d\n                return 0\n                ;;\n        esac\n    else\n        cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    fi\n\n    if [[ \"$cur\" == \"--\"* ]]; then\n        # there is currently only one argument that takes --\n\tCOMPREPLY=($(compgen -P '--' -W 'version' -- \"${cur:2}\"))\n    else\n\tdir=\".\"\n\tline=$(echo ${COMP_LINE} | cut -d\" \" -f 2-)\n        # filter out all non relevant arguments but keep C for dirs\n\twhile getopts :C:f:j:l:k:nvd:t: opt $line; do\n\t    case $opt in\n                # eval for tilde expansion\n\t\tC) eval dir=\"$OPTARG\" ;;\n\t    esac\n\tdone;\n\ttargets_command=\"eval ninja -C \\\"${dir}\\\" -t targets all 2>/dev/null | cut -d: -f1\"\n\tCOMPREPLY=($(compgen -W '`${targets_command}`' -- \"$cur\"))\n    fi\n    return\n}\ncomplete -F _ninja_target ninja\n"
  },
  {
    "path": "misc/ci.py",
    "content": "#!/usr/bin/env python3\n\nimport os\n\nignores = [\n\t'.git/',\n\t'misc/afl-fuzz-tokens/',\n\t'src/depfile_parser.cc',\n\t'src/lexer.cc',\n]\n\nerror_count = 0\n\ndef error(path: str, msg: str) -> None:\n\tglobal error_count\n\terror_count += 1\n\tprint('\\x1b[1;31m{}\\x1b[0;31m{}\\x1b[0m'.format(path, msg))\n\ntry:\n\timport git\n\trepo = git.Repo('.')\nexcept Exception:\n\trepo = None\n\nfor root, directory, filenames in os.walk('.'):\n\tfor filename in filenames:\n\t\tpath = os.path.join(root, filename)[2:]\n\t\tif any([path.startswith(x) for x in ignores]) or (repo is not None and repo.ignored(path)):\n\t\t\tcontinue\n\t\twith open(path, 'rb') as file:\n\t\t\tline_nr = 1\n\t\t\ttry:\n\t\t\t\tfor line in [x.decode() for x in file.readlines()]:\n\t\t\t\t\tif len(line) == 0 or line[-1] != '\\n':\n\t\t\t\t\t\terror(path, ' missing newline at end of file.')\n\t\t\t\t\tif len(line) > 1:\n\t\t\t\t\t\tif line[-2] == '\\r':\n\t\t\t\t\t\t\terror(path, ' has Windows line endings.')\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tif line[-2] == ' ' or line[-2] == '\\t':\n\t\t\t\t\t\t\terror(path, ':{} has trailing whitespace.'.format(line_nr))\n\t\t\t\t\tline_nr += 1\n\t\t\texcept UnicodeError:\n\t\t\t\tpass # binary file\n\nexit(error_count)\n"
  },
  {
    "path": "misc/inherited-fds.ninja",
    "content": "# This build file prints out a list of open file descriptors in\n# Ninja subprocesses, to help verify we don't accidentally leak\n# any.\n\n# Because one fd leak was in the code managing multiple subprocesses,\n# this test brings up multiple subprocesses and then dumps the fd\n# table of the last one.\n\n# Use like: ./ninja -f misc/inherited-fds.ninja\n\nrule sleep\n  command = sleep 10000\n\nrule dump\n  command = sleep 1; ls -l /proc/self/fd; exit 1\n\nbuild all: phony a b c d e\n\nbuild a: sleep\nbuild b: sleep\nbuild c: sleep\nbuild d: sleep\nbuild e: dump\n"
  },
  {
    "path": "misc/jobserver_pool.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2024 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Setup a GNU Make Jobserver jobs pool then launch a command with it.\n\nOn Windows, this only supports the semaphore-based scheme.\nOn Posix, this uses a fifo by default, use --pipe for pipe mode.\n\nOn exit, this script verifies that all job slots were returned to\nthe pool, and will print an error message if this is not the case.\n\nThis is useful to catch the use of broken protocol clients.\nUse the `--no-check` flag to disable this.\n\nSee --help-usage for usage examples.\n\"\"\"\nimport argparse\nimport os\nimport platform\nimport subprocess\nimport sys\nimport typing as T\n\n# Technical note about the MAKEFLAGS values set by this script.\n#\n# All the MAKEFLAGS values created by this script begin\n# with \" -j{count} \", i.e. an initial space, the \"-j\" characters\n# followed by a job slot count then another space.\n#\n# The initial space is only there to mimic what GNU Make 4.3\n# does. Other pool implementations do not use one and thus\n# clients should not expect it (even GNU Make doesn't seem\n# to care when used as a jobserver client).\n#\n# The {count} value is also not available in many pool\n# implementations, but is useful to better debug multi-builds\n# using this script (i.e. to verify that the pool has the\n# expected size). Protocol clients should not depend on it\n# though.\n\n_DEFAULT_NAME = \"jobserver_pool\"\n_IS_WINDOWS = sys.platform in (\"win32\", \"cygwin\")\n\nif _IS_WINDOWS:\n\n    try:\n        # This requires pywin32 to be installed.\n        import pywintypes\n        import win32event\n        import win32api\n    except ModuleNotFoundError:\n        print(\n            \"\\nERROR: Could not import Win32 API, please install pywin32, e.g. `python -m pip install pywin32`.\\n\",\n            file=sys.stderr,\n        )\n        raise\n\n    # It seems impossible to import a proper mypy-compatible type definition for PyHANDLE\n    # 'from pywintypes import PyHANDLE' fails stating there is no such name.\n    # 'from pywintypes import HANDLE as PyHANDLE' fails because HANDLE is a function, not a type.\n    PyHandle: T.TypeAlias = T.Any\n\n    def create_sem(\n        sem_name: str, jobs_count: int\n    ) -> T.Tuple[PyHandle, T.Dict[str, str]]:\n        \"\"\"Create and initialize Win32 semaphore.\"\"\"\n        assert jobs_count > 0, f\"Jobs count must be strictly positive\"\n        # The win32event documentation states that the first argument to CreateSemaphore()\n        # can be None to indicate default security attributes, but mypy only wants\n        # a PySECURITY_ATTRIBUTES for some reason.\n        handle = win32event.CreateSemaphore(\n            None,  # type: ignore\n            jobs_count - 1,\n            jobs_count - 1,\n            sem_name,\n        )\n        assert bool(handle), f\"Error creating Win32 semaphore {win32api.GetLastError()}\"\n        # See technical note above about MAKEFLAGS format.\n        env = dict(os.environ)\n        env[\"MAKEFLAGS\"] = f\" -j{jobs_count} --jobserver-auth=\" + sem_name\n        return handle, env\n\n    def check_sem_count(handle: PyHandle, jobs_count: int) -> int:\n        if jobs_count <= 1:\n            # Nothing to check here.\n            return 0\n\n        expected_count = jobs_count - 1\n\n        read_count = win32event.ReleaseSemaphore(handle, 1)\n        if read_count < expected_count:\n            print(\n                f\"ERROR: {expected_count - read_count} were missing from the jobs pool (got {read_count}, expected {expected_count})\",\n                file=sys.stderr,\n            )\n            return 1\n        if read_count > expected_count:\n            print(\n                f\"ERROR: {read_count - expected_count} extra tokens were released to the jobs pool (got {read_count}, expected {expected_count})\",\n                file=sys.stderr,\n            )\n            return 1\n\n        return 0\n\n    def print_usage() -> int:\n        print(\n            r\"\"\"Example usage:\n\n# Start <command> after setting the server to provide as many jobs\n# as available CPUs (the default)\npython \\path\\to\\jobserver_pool.py <command>\n\n# Start <command> with a fixed number of job slots.\npython \\path\\to\\jobserver_pool.py -j10 <command>\n\n# Disable the feature with a non-positive count. This is equivalent\n# to running <command> directly.\npython \\path\\to\\jobserver_pool.py -j0 <command>\n\n# Use a specific semaphore name\npython \\path\\to\\jobserver_pool.py --name=my_build_jobs <command>\n\n# Setup jobserver then start new interactive PowerShell\n# session, print MAKEFLAGS value, build stuff, then exit.\npython \\path\\to\\jobserver_pool.py powershell.exe\n$env:MAKEFLAGS\n... build stuff ...\nexit\n\"\"\"\n        )\n        return 0\n\nelse:  # !_IS_WINDOWS\n\n    def create_pipe(jobs_count: int) -> T.Tuple[int, int, T.Dict[str, str]]:\n        \"\"\"Create and fill Posix PIPE.\"\"\"\n        read_fd, write_fd = os.pipe()\n        os.set_inheritable(read_fd, True)\n        os.set_inheritable(write_fd, True)\n        assert jobs_count > 0, f\"Token count must be strictly positive\"\n        os.write(write_fd, (jobs_count - 1) * b\"x\")\n        # See technical note above about MAKEFLAGS format.\n        env = dict(os.environ)\n        env[\"MAKEFLAGS\"] = (\n            f\" -j{jobs_count} --jobserver-fds={read_fd},{write_fd} --jobserver-auth={read_fd},{write_fd}\"\n        )\n        return read_fd, write_fd, env\n\n    def create_fifo(path: str, jobs_count: int) -> T.Tuple[int, int, T.Dict[str, str]]:\n        \"\"\"Create and fill Posix FIFO.\"\"\"\n        if os.path.exists(path):\n            os.remove(path)\n\n        # mypy complains that this does not exit on Windows.\n        os.mkfifo(path)  # type: ignore\n\n        read_fd = os.open(path, os.O_RDONLY | os.O_NONBLOCK)\n        write_fd = os.open(path, os.O_WRONLY | os.O_NONBLOCK)\n        assert jobs_count > 0, f\"Token count must be strictly positive\"\n        os.write(write_fd, (jobs_count - 1) * b\"x\")\n        # See technical note above about MAKEFLAGS format.\n        env = dict(os.environ)\n        env[\"MAKEFLAGS\"] = f\" -j{jobs_count} --jobserver-auth=fifo:\" + path\n        return read_fd, write_fd, env\n\n    def print_usage() -> int:\n        print(\n            r\"\"\"Example usage:\n\n# Start <command> after setting the job pool to provide as many jobs\n# as available CPUs (the default)\n/path/to/jobserver_pool.py <command>\n\n# Start <command> with a fixed number of jobs\n/path/to/jobserver_pool.py -j10 <command>\n\n# Disable the feature with a non-positive count. This is equivalent\n# to running <command> directly.\n/path/to/jobserver_pool.py -j0 <command>\n\n# Use a specific FIFO path\n/path/to/jobserver_pool.py --fifo=/tmp/my_build_jobs <command>\n\n# Setup jobserver then start new interactive Bash shell\n# session, print MAKEFLAGS value, build stuff, then exit.\n/path/to/jobserver_pool.py bash -i\necho \"$MAKEFLAGS\"\n... build stuff ...\nexit\n\"\"\"\n        )\n        return 0\n\n    def check_pipe_tokens(read_fd: int, jobs_count: int) -> int:\n        if jobs_count <= 1:  # Nothing to check\n            return 0\n\n        # Remove implicit token from the expected count.\n        expected_count = jobs_count - 1\n        os.set_blocking(read_fd, False)\n        read_count = 0\n        while True:\n            try:\n                token = os.read(read_fd, 1)\n                if len(token) == 0:  # End of pipe?\n                    break\n                read_count += 1\n            except BlockingIOError:\n                break\n\n        if read_count < expected_count:\n            print(\n                f\"ERROR: {expected_count - read_count} tokens were missing from the jobs pool (got {read_count}, expected {expected_count})\",\n                file=sys.stderr,\n            )\n            return 1\n        if read_count > expected_count:\n            print(\n                f\"ERROR: {read_count - expected_count} extra tokens were released to the jobs pool (got {read_count}, expected {expected_count})\",\n                file=sys.stderr,\n            )\n            return 1\n\n        return 0\n\n\ndef main() -> int:\n    parser = argparse.ArgumentParser(\n        description=__doc__, formatter_class=argparse.RawTextHelpFormatter\n    )\n    if _IS_WINDOWS:\n        parser.add_argument(\n            \"--name\",\n            help=f\"Specify semaphore name, default is {_DEFAULT_NAME}\",\n            default=_DEFAULT_NAME,\n        )\n    else:\n        mutex_group = parser.add_mutually_exclusive_group()\n        mutex_group.add_argument(\n            \"--pipe\",\n            action=\"store_true\",\n            help=\"Implement the pool with a Unix pipe (default is FIFO).\",\n        )\n        mutex_group.add_argument(\n            \"--fifo\",\n            default=_DEFAULT_NAME,\n            help=f\"Specify pool FIFO file path (default ./{_DEFAULT_NAME})\",\n        )\n\n    parser.add_argument(\n        \"--no-check\",\n        action=\"store_true\",\n        help=\"Disable the final check that verifies that all job slots were returned to the pool on exit.\",\n    )\n\n    parser.add_argument(\n        \"--help-usage\", action=\"store_true\", help=\"Print usage examples.\"\n    )\n\n    parser.add_argument(\n        \"-j\",\n        \"--jobs\",\n        action=\"store\",\n        metavar=\"COUNT\",\n        dest=\"jobs_count\",\n        type=int,\n        default=os.cpu_count(),\n        help=\"Set job slots ccount, default is available CPUs count\",\n    )\n\n    parser.add_argument(\"command\", nargs=argparse.REMAINDER, help=\"Command to run.\")\n    args = parser.parse_args()\n\n    if args.help_usage:\n        return print_usage()\n\n    if not args.command:\n        parser.error(\"This script requires at least one command argument!\")\n\n    jobs_count = args.jobs_count\n    if jobs_count <= 0:\n        # Disable the feature.\n        ret = subprocess.run(args.command)\n        exit_code = ret.returncode\n    elif _IS_WINDOWS:\n        # Run with a Window semaphore.\n        try:\n            handle, env = create_sem(args.name, jobs_count)\n            ret = subprocess.run(args.command, env=env)\n            exit_code = ret.returncode\n\n            if exit_code == 0 and not args.no_check:\n                exit_code = check_sem_count(handle, jobs_count)\n\n        finally:\n            win32api.CloseHandle(handle)\n    else:\n        # Run with pipe descriptors by default, or a FIFO if --fifo is used.\n        exit_code = 0\n        fifo_path = \"\"\n        try:\n            if not args.pipe:\n                fifo_path = os.path.abspath(args.fifo)\n                read_fd, write_fd, env = create_fifo(fifo_path, args.jobs_count)\n                ret = subprocess.run(args.command, env=env)\n            else:\n                read_fd, write_fd, env = create_pipe(args.jobs_count)\n                ret = subprocess.run(\n                    args.command, env=env, pass_fds=(read_fd, write_fd)\n                )\n\n            exit_code = ret.returncode\n            if exit_code == 0 and not args.no_check:\n                exit_code = check_pipe_tokens(read_fd, jobs_count)\n\n        finally:\n            os.close(read_fd)\n            os.close(write_fd)\n\n            if fifo_path:\n                os.remove(fifo_path)\n\n    return exit_code\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "misc/jobserver_pool_test.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"Regression tests for the jobserver_pool.py script.\"\"\"\n\nimport os\nimport re\nimport platform\nimport subprocess\nimport sys\nimport tempfile\nimport unittest\nimport typing as T\n\n_SCRIPT_DIR = os.path.dirname(__file__)\n_JOBSERVER_SCRIPT = os.path.join(_SCRIPT_DIR, \"jobserver_pool.py\")\n_JOBSERVER_CMD = [sys.executable, _JOBSERVER_SCRIPT]\n\n_IS_WINDOWS = sys.platform == \"win32\"\n\n# This is only here to avoid depending on the non-standard\n# scanf package which does the job properly :-)\n\n\ndef _simple_scanf(pattern: str, input: str) -> T.Sequence[T.Any]:\n    \"\"\"Extract values from input using a scanf-like pattern.\n\n    This is very basic and only used to avoid depending on the\n    non-standard scanf package which does the job properly.\n    Only supports %d, %s and %%, does not support any fancy\n    escaping.\n    \"\"\"\n    re_pattern = \"\"\n    groups = \"\"\n    from_pos = 0\n\n    # Just in case.\n    assert \".\" not in pattern, f\"Dots in pattern not supported.\"\n    assert \"?\" not in pattern, f\"Question marks in pattern not supported.\"\n\n    while True:\n        next_percent = pattern.find(\"%\", from_pos)\n        if next_percent < 0 or next_percent + 1 >= len(pattern):\n            re_pattern += pattern[from_pos:]\n            break\n\n        re_pattern += pattern[from_pos:next_percent]\n\n        from_pos = next_percent + 2\n        formatter = pattern[next_percent + 1]\n        if formatter == \"%\":\n            re_pattern += \"%\"\n        elif formatter == \"d\":\n            groups += formatter\n            re_pattern += \"(\\\\d+)\"\n        elif formatter == \"s\":\n            groups += formatter\n            re_pattern += \"(\\\\S+)\"\n        else:\n            assert False, f\"Unsupported scanf formatter: %{formatter}\"\n\n    m = re.match(re_pattern, input)\n    if not m:\n        return None\n\n    result = []\n    for group_index, formatter in enumerate(groups, start=1):\n        if formatter == \"d\":\n            result.append(int(m.group(group_index)))\n        elif formatter == \"s\":\n            result.append(m.group(group_index))\n        else:\n            assert False, f\"Unsupported formatter {formatter}\"\n\n    return result\n\n\nclass JobserverPool(unittest.TestCase):\n    def _run_jobserver_echo_MAKEFLAGS(\n        self, cmd_args_prefix\n    ) -> \"subprocess.CompletedProcess[str]\":\n        if _IS_WINDOWS:\n            cmd_args = cmd_args_prefix + [\"cmd.exe\", \"/c\", \"echo %MAKEFLAGS%\"]\n        else:\n            cmd_args = cmd_args_prefix + [\"sh\", \"-c\", 'echo \"$MAKEFLAGS\"']\n\n        ret = subprocess.run(\n            cmd_args,\n            text=True,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n        )\n        ret.check_returncode()\n        return ret\n\n    def _test_echo_MAKEFLAGS(self, cmd_args_prefix, expected_core_count: int):\n        ret = self._run_jobserver_echo_MAKEFLAGS(cmd_args_prefix)\n        makeflags = ret.stdout.rstrip()\n\n        if expected_core_count == 0:\n            if _IS_WINDOWS:\n                # On Windows, echo %FOO% prints \"%FOO%\" if FOO is not defined!\n                self.assertEqual(makeflags.strip(), \"%MAKEFLAGS%\")\n            else:\n                self.assertEqual(makeflags.strip(), \"\")\n\n        else:  # expected_core_count > 0\n            if _IS_WINDOWS:\n                expected_format = \" -j%d --jobserver-auth=%s\"\n            else:\n                expected_format = \" -j%d --jobserver-auth=fifo:%s\"\n\n            m = _simple_scanf(expected_format, makeflags)\n            self.assertTrue(\n                m,\n                f\"Invalid MAKEFLAGS value, expected format [{expected_format}], got: [{makeflags}]\",\n            )\n\n            if _IS_WINDOWS:\n                sem_name = m[1]\n                self.assertEqual(\n                    sem_name,\n                    \"jobserver_pool\",\n                    f\"Invalid semaphore name in MAKEFLAGS value [{makeflags}]\",\n                )\n            else:\n                fifo_name = os.path.basename(m[1])\n                self.assertEqual(\n                    fifo_name,\n                    \"jobserver_pool\",\n                    f\"Invalid fifo name in MAKEFLAGS value [{makeflags}]\",\n                )\n\n            core_count = m[0]\n            self.assertEqual(\n                core_count,\n                expected_core_count,\n                f\"Invalid core count {core_count}, expected {expected_core_count}\",\n            )\n\n    def test_MAKEFLAGS_default(self):\n        self._test_echo_MAKEFLAGS(_JOBSERVER_CMD, os.cpu_count())\n\n    def test_MAKEFLAGS_with_10_jobs(self):\n        self._test_echo_MAKEFLAGS(_JOBSERVER_CMD + [\"-j10\"], 10)\n        self._test_echo_MAKEFLAGS(_JOBSERVER_CMD + [\"--jobs=10\"], 10)\n        self._test_echo_MAKEFLAGS(_JOBSERVER_CMD + [\"--jobs\", \"10\"], 10)\n\n    def test_MAKEFLAGS_with_no_jobs(self):\n        self._test_echo_MAKEFLAGS(_JOBSERVER_CMD + [\"-j0\"], 0)\n        self._test_echo_MAKEFLAGS(_JOBSERVER_CMD + [\"--jobs=0\"], 0)\n        self._test_echo_MAKEFLAGS(_JOBSERVER_CMD + [\"--jobs\", \"0\"], 0)\n\n    @unittest.skipIf(_IS_WINDOWS, \"--fifo is not supported on Windows\")\n    def test_MAKEFLAGS_with_fifo(self):\n        fifo_name = \"test_fifo\"\n        fifo_path = os.path.abspath(fifo_name)\n        ret = self._run_jobserver_echo_MAKEFLAGS(\n            _JOBSERVER_CMD + [\"-j10\", \"--fifo\", fifo_name]\n        )\n        makeflags = ret.stdout.rstrip()\n        self.assertEqual(makeflags, \" -j10 --jobserver-auth=fifo:\" + fifo_path)\n\n    @unittest.skipIf(not _IS_WINDOWS, \"--name is not supported on Posix\")\n    def test_MAKEFLAGS_with_name(self):\n        sem_name = \"test_semaphore\"\n        ret = self._run_jobserver_echo_MAKEFLAGS(\n            _JOBSERVER_CMD + [\"-j10\", \"--name\", sem_name]\n        )\n        makeflags = ret.stdout.rstrip()\n        self.assertEqual(makeflags, \" -j10 --jobserver-auth=\" + sem_name)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "misc/jobserver_test.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2024 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom textwrap import dedent\nimport os\nimport platform\nimport subprocess\nimport tempfile\nimport typing as T\nimport shlex\nimport sys\nimport unittest\n\n_SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__))\n_JOBSERVER_POOL_SCRIPT = os.path.join(_SCRIPT_DIR, \"jobserver_pool.py\")\n_JOBSERVER_TEST_HELPER_SCRIPT = os.path.join(_SCRIPT_DIR, \"jobserver_test_helper.py\")\n\n_PLATFORM_IS_WINDOWS = platform.system() == \"Windows\"\n\n# Set this to True to debug command invocations.\n_DEBUG = False\n\ndefault_env = dict(os.environ)\ndefault_env.pop(\"NINJA_STATUS\", None)\ndefault_env.pop(\"MAKEFLAGS\", None)\ndefault_env[\"TERM\"] = \"dumb\"\nNINJA_PATH = os.path.abspath(\"./ninja\")\n\n\nclass BuildDir:\n    def __init__(self, build_ninja: str):\n        self.build_ninja = dedent(build_ninja)\n        self.d: T.Optional[tempfile.TemporaryDirectory] = None\n\n    def __enter__(self):\n        self.d = tempfile.TemporaryDirectory()\n        with open(os.path.join(self.d.name, \"build.ninja\"), \"w\") as f:\n            f.write(self.build_ninja)\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.d.cleanup()\n\n    @property\n    def path(self) -> str:\n        assert self.d\n        return self.d.name\n\n    def run(\n        self,\n        cmd_flags: T.Sequence[str] = [],\n        env: T.Dict[str, str] = default_env,\n    ) -> None:\n        \"\"\"Run a command, raise exception on error. Do not capture outputs.\"\"\"\n        ret = subprocess.run(cmd_flags, env=env)\n        ret.check_returncode()\n\n    def ninja_run(\n        self,\n        ninja_args: T.List[str],\n        prefix_args: T.List[str] = [],\n        extra_env: T.Dict[str, str] = {},\n    ) -> \"subprocess.CompletedProcess[str]\":\n        ret = self.ninja_spawn(\n            ninja_args,\n            prefix_args=prefix_args,\n            extra_env=extra_env,\n            capture_output=False,\n        )\n        ret.check_returncode()\n        return ret\n\n    def ninja_clean(self) -> None:\n        self.ninja_run([\"-t\", \"clean\"])\n\n    def ninja_spawn(\n        self,\n        ninja_args: T.List[str],\n        prefix_args: T.List[str] = [],\n        extra_env: T.Dict[str, str] = {},\n        capture_output: bool = True,\n    ) -> \"subprocess.CompletedProcess[str]\":\n        \"\"\"Run Ninja command and capture outputs.\"\"\"\n        cmd_args = prefix_args + [NINJA_PATH, \"-C\", self.path] + ninja_args\n        if _DEBUG:\n            cmd_str = \" \".join(shlex.quote(c) for c in cmd_args)\n            print(f\"CMD [{cmd_str}]\", file=sys.stderr)\n        return subprocess.run(\n            cmd_args,\n            text=True,\n            stdout=subprocess.PIPE if capture_output else None,\n            stderr=subprocess.PIPE if capture_output else None,\n            env={**default_env, **extra_env},\n        )\n\n\ndef span_output_file(span_n: int) -> str:\n    return \"out%02d\" % span_n\n\n\ndef generate_build_plan(command_count: int) -> str:\n    \"\"\"Generate a Ninja build plan for |command_count| parallel tasks.\n\n    Each task calls the test helper script which waits for 50ms\n    then writes its own start and end time to its output file.\n    \"\"\"\n    result = f\"\"\"\nrule span\n    command = {sys.executable} -S {_JOBSERVER_TEST_HELPER_SCRIPT} --duration-ms=50 $out\n\n\"\"\"\n\n    for n in range(command_count):\n        result += \"build %s: span\\n\" % span_output_file(n)\n\n    result += \"build all: phony %s\\n\" % \" \".join(\n        [span_output_file(n) for n in range(command_count)]\n    )\n    return result\n\n\ndef compute_max_overlapped_spans(build_dir: str, command_count: int) -> int:\n    \"\"\"Compute the maximum number of overlapped spanned tasks.\n\n    This reads the output files from |build_dir| and look at their start and end times\n    to compute the maximum number of tasks that were run in parallel.\n    \"\"\"\n    # Read the output files.\n    if command_count < 2:\n        return 0\n\n    spans: T.List[T.Tuple[int, int]] = []\n    for n in range(command_count):\n        with open(os.path.join(build_dir, span_output_file(n)), \"rb\") as f:\n            content = f.read().decode(\"utf-8\")\n        lines = content.splitlines()\n        assert len(lines) == 2, f\"Unexpected output file content: [{content}]\"\n        spans.append((int(lines[0]), int(lines[1])))\n\n    # Stupid but simple, for each span, count the number of other spans that overlap it.\n    max_overlaps = 1\n    for n in range(command_count):\n        cur_start, cur_end = spans[n]\n        cur_overlaps = 1\n        for m in range(command_count):\n            other_start, other_end = spans[m]\n            if n != m and other_end > cur_start and other_start < cur_end:\n                cur_overlaps += 1\n\n        if cur_overlaps > max_overlaps:\n            max_overlaps = cur_overlaps\n\n    return max_overlaps\n\n\nclass JobserverTest(unittest.TestCase):\n\n    def test_no_jobserver_client(self):\n        task_count = 4\n        build_plan = generate_build_plan(task_count)\n        with BuildDir(build_plan) as b:\n            output = b.run([NINJA_PATH, \"-C\", b.path, f\"-j{task_count}\", \"all\"])\n\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, task_count)\n\n            b.ninja_clean()\n            output = b.run([NINJA_PATH, \"-C\", b.path, \"-j1\", \"all\"])\n\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, 1)\n\n    def _run_client_test(self, jobserver_args: T.List[str]) -> None:\n        task_count = 4\n        build_plan = generate_build_plan(task_count)\n        with BuildDir(build_plan) as b:\n            # First, run the full tasks with with {task_count} tokens, this should allow all\n            # tasks to run in parallel.\n            ret = b.ninja_run(\n                ninja_args=[\"all\"],\n                prefix_args=jobserver_args + [f\"--jobs={task_count}\"],\n            )\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, task_count)\n\n            # Second, use 2 tokens only, and verify that this was enforced by Ninja.\n            b.ninja_clean()\n            b.ninja_run(\n                [\"all\"],\n                prefix_args=jobserver_args + [\"--jobs=2\"],\n            )\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, 2)\n\n            # Third, verify that --jobs=1 serializes all tasks.\n            b.ninja_clean()\n            b.ninja_run(\n                [\"all\"],\n                prefix_args=jobserver_args + [\"--jobs=1\"],\n            )\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, 1)\n\n            # Finally, verify that -j1 overrides the pool.\n            b.ninja_clean()\n            b.ninja_run(\n                [\"-j1\", \"all\"],\n                prefix_args=jobserver_args + [f\"--jobs={task_count}\"],\n            )\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, 1)\n\n            # On Linux, use taskset to limit the number of available cores to 1\n            # and verify that the jobserver overrides the default Ninja parallelism\n            # and that {task_count} tasks are still spawned in parallel.\n            if platform.system() == \"Linux\":\n                # First, run without a jobserver, with a single CPU, Ninja will\n                # use a parallelism of 2 in this case (GuessParallelism() in ninja.cc)\n                b.ninja_clean()\n                b.ninja_run(\n                    [\"all\"],\n                    prefix_args=[\"taskset\", \"-c\", \"0\"],\n                )\n                max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n                self.assertEqual(max_overlaps, 2)\n\n                # Now with a jobserver with {task_count} tasks.\n                b.ninja_clean()\n                b.ninja_run(\n                    [\"all\"],\n                    prefix_args=jobserver_args\n                    + [f\"--jobs={task_count}\"]\n                    + [\"taskset\", \"-c\", \"0\"],\n                )\n                max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n                self.assertEqual(max_overlaps, task_count)\n\n    @unittest.skipIf(_PLATFORM_IS_WINDOWS, \"These test methods do not work on Windows\")\n    def test_jobserver_client_with_posix_fifo(self):\n        self._run_client_test([sys.executable, \"-S\", _JOBSERVER_POOL_SCRIPT])\n\n    @unittest.skipIf(_PLATFORM_IS_WINDOWS, \"These test methods do not work on Windows\")\n    def test_jobserver_client_with_posix_pipe(self):\n        # Verify that setting up a --pipe server does not make Ninja exit with an error.\n        # Instead, a warning is printed.\n        task_count = 4\n        build_plan = generate_build_plan(task_count)\n        with BuildDir(build_plan) as b:\n\n            prefix_args = [\n                sys.executable,\n                \"-S\",\n                _JOBSERVER_POOL_SCRIPT,\n                \"--pipe\",\n                f\"--jobs={task_count}\",\n            ]\n\n            def run_ninja_with_jobserver_pipe(args):\n                ret = b.ninja_spawn(args, prefix_args=prefix_args)\n                ret.check_returncode()\n                return ret.stdout, ret.stderr\n\n            output, error = run_ninja_with_jobserver_pipe([\"all\"])\n            if _DEBUG:\n                print(f\"OUTPUT [{output}]\\nERROR [{error}]\\n\", file=sys.stderr)\n            self.assertTrue(error.find(\"Pipe-based protocol is not supported!\") >= 0)\n\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, task_count)\n\n            # Using an explicit -j<N> ignores the jobserver pool.\n            b.ninja_clean()\n            output, error = run_ninja_with_jobserver_pipe([\"-j1\", \"all\"])\n            if _DEBUG:\n                print(f\"OUTPUT [{output}]\\nERROR [{error}]\\n\", file=sys.stderr)\n            self.assertFalse(error.find(\"Pipe-based protocol is not supported!\") >= 0)\n\n            max_overlaps = compute_max_overlapped_spans(b.path, task_count)\n            self.assertEqual(max_overlaps, 1)\n\n    def _test_MAKEFLAGS_value(\n        self, ninja_args: T.List[str] = [], prefix_args: T.List[str] = []\n    ):\n        build_plan = r\"\"\"\nrule print\n    command = echo MAKEFLAGS=\"[$$MAKEFLAGS]\"\n\nbuild all: print\n\"\"\"\n        with BuildDir(build_plan) as b:\n            ret = b.ninja_spawn(\n                ninja_args + [\"--quiet\", \"all\"], prefix_args=prefix_args\n            )\n            self.assertEqual(ret.returncode, 0)\n            output = ret.stdout.strip()\n            pos = output.find(\"MAKEFLAGS=[\")\n            self.assertNotEqual(pos, -1, \"Could not find MAKEFLAGS in output!\")\n            makeflags, sep, _ = output[pos + len(\"MAKEFLAGS=[\") :].partition(\"]\")\n            self.assertEqual(sep, \"]\", \"Missing ] in output!: \" + output)\n            self.assertTrue(\n                \"--jobserver-auth=\" in makeflags,\n                f\"Missing --jobserver-auth from MAKEFLAGS [{makeflags}]\\nSTDOUT [{ret.stdout}]\\nSTDERR [{ret.stderr}]\",\n            )\n\n    def test_client_passes_MAKEFLAGS(self):\n        self._test_MAKEFLAGS_value(\n            prefix_args=[sys.executable, \"-S\", _JOBSERVER_POOL_SCRIPT]\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "misc/jobserver_test_helper.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2024 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Simple utility used by the jobserver test. Wait for specific time, then write start/stop times to output file.\"\"\"\n\nimport argparse\nimport time\nimport sys\nfrom pathlib import Path\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\n        \"--duration-ms\",\n        default=\"50\",\n        help=\"sleep duration in milliseconds (default 50)\",\n    )\n    parser.add_argument(\"output_file\", type=Path, help=\"output file name.\")\n    args = parser.parse_args()\n\n    now_time_ns = time.time_ns()\n    time.sleep(int(args.duration_ms) / 1000.0)\n    args.output_file.write_text(f\"{now_time_ns}\\n{time.time_ns()}\\n\")\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "misc/long-slow-build.ninja",
    "content": "# An input file for running a \"slow\" build.\n# Use like: ninja -f misc/long-slow-build.ninja all\n\nrule sleep\n  command = sleep 1\n  description = SLEEP $out\n\nbuild 0: sleep README\nbuild 1: sleep README\nbuild 2: sleep README\nbuild 3: sleep README\nbuild 4: sleep README\nbuild 5: sleep README\nbuild 6: sleep README\nbuild 7: sleep README\nbuild 8: sleep README\nbuild 9: sleep README\nbuild 10: sleep 0\nbuild 11: sleep 1\nbuild 12: sleep 2\nbuild 13: sleep 3\nbuild 14: sleep 4\nbuild 15: sleep 5\nbuild 16: sleep 6\nbuild 17: sleep 7\nbuild 18: sleep 8\nbuild 19: sleep 9\nbuild 20: sleep 10\nbuild 21: sleep 11\nbuild 22: sleep 12\nbuild 23: sleep 13\nbuild 24: sleep 14\nbuild 25: sleep 15\nbuild 26: sleep 16\nbuild 27: sleep 17\nbuild 28: sleep 18\nbuild 29: sleep 19\nbuild all: phony 20 21 22 23 24 25 26 27 28 29\n"
  },
  {
    "path": "misc/manifest_fuzzer.cc",
    "content": "// Copyright 2020 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"stdint.h\"\n#include <string>\n#include \"disk_interface.h\"\n#include \"state.h\"\n#include \"manifest_parser.h\"\n#include <filesystem>\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar build_file[256];\n\tsprintf(build_file, \"/tmp/build.ninja\");\n\tFILE *fp = fopen(build_file, \"wb\");\n\tif (!fp)\n\t\treturn 0;\n\tfwrite(data, size, 1, fp);\n\tfclose(fp);\n\n\tstd::string err;\n\tRealDiskInterface disk_interface;\n\tState state;\n\tManifestParser parser(&state, &disk_interface);\n\n\tparser.Load(\"/tmp/build.ninja\", &err);\n\n\tstd::__fs::filesystem::remove_all(\"/tmp/build.ninja\");\n\treturn 0;\n}\n"
  },
  {
    "path": "misc/measure.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2011 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"measure the runtime of a command by repeatedly running it.\n\"\"\"\n\nimport time\nimport subprocess\nimport sys\nfrom typing import Union, List\n\ndevnull = open('/dev/null', 'w')\n\ndef run(cmd: Union[str, List[str]], repeat: int = 10) -> None:\n    print('sampling:', end=' ')\n    sys.stdout.flush()\n\n    samples = []\n    for _ in range(repeat):\n        start = time.time()\n        subprocess.call(cmd, stdout=devnull, stderr=devnull)\n        end = time.time()\n        dt = (end - start) * 1000\n        print('%dms' % int(dt), end=' ')\n        sys.stdout.flush()\n        samples.append(dt)\n    print()\n\n    # We're interested in the 'pure' runtime of the code, which is\n    # conceptually the smallest time we'd see if we ran it enough times\n    # such that it got the perfect time slices / disk cache hits.\n    best = min(samples)\n    # Also print how varied the outputs were in an attempt to make it\n    # more obvious if something has gone terribly wrong.\n    err = sum(s - best for s in samples) / float(len(samples))\n    print('estimate: %dms (mean err %.1fms)' % (best, err))\n\nif __name__ == '__main__':\n    if len(sys.argv) < 2:\n        print('usage: measure.py command args...')\n        sys.exit(1)\n    run(cmd=sys.argv[1:])\n"
  },
  {
    "path": "misc/ninja.vim",
    "content": "\" ninja build file syntax.\n\" Language: ninja build file as described at\n\"           http://ninja-build.org/manual.html\n\" Version: 1.5\n\" Last Change: 2018/04/05\n\" Maintainer: Nicolas Weber <nicolasweber@gmx.de>\n\" Version 1.4 of this script is in the upstream vim repository and will be\n\" included in the next vim release. If you change this, please send your change\n\" upstream.\n\n\" ninja lexer and parser are at\n\" https://github.com/ninja-build/ninja/blob/master/src/lexer.in.cc\n\" https://github.com/ninja-build/ninja/blob/master/src/manifest_parser.cc\n\nif exists(\"b:current_syntax\")\n  finish\nendif\n\nlet s:cpo_save = &cpo\nset cpo&vim\n\nsyn case match\n\n\" Comments are only matched when the # is at the beginning of the line (with\n\" optional whitespace), as long as the prior line didn't end with a $\n\" continuation.\nsyn match ninjaComment /\\(\\$\\n\\)\\@<!\\_^\\s*#.*$/  contains=@Spell\n\n\" Toplevel statements are the ones listed here and\n\" toplevel variable assignments (ident '=' value).\n\" lexer.in.cc, ReadToken() and manifest_parser.cc, Parse()\nsyn match ninjaKeyword \"^build\\>\"\nsyn match ninjaKeyword \"^rule\\>\"\nsyn match ninjaKeyword \"^pool\\>\"\nsyn match ninjaKeyword \"^default\\>\"\nsyn match ninjaKeyword \"^include\\>\"\nsyn match ninjaKeyword \"^subninja\\>\"\n\n\" Both 'build' and 'rule' begin a variable scope that ends\n\" on the first line without indent. 'rule' allows only a\n\" limited set of magic variables, 'build' allows general\n\" let assignments.\n\" manifest_parser.cc, ParseRule()\nsyn region ninjaRule start=\"^rule\" end=\"^\\ze\\S\" contains=TOP transparent\nsyn keyword ninjaRuleCommand contained containedin=ninjaRule command\n                                     \\ deps depfile description generator\n                                     \\ pool restat rspfile rspfile_content\n\nsyn region ninjaPool start=\"^pool\" end=\"^\\ze\\S\" contains=TOP transparent\nsyn keyword ninjaPoolCommand contained containedin=ninjaPool  depth\n\n\" Strings are parsed as follows:\n\" lexer.in.cc, ReadEvalString()\n\" simple_varname = [a-zA-Z0-9_-]+;\n\" varname = [a-zA-Z0-9_.-]+;\n\" $$ -> $\n\" $\\n -> line continuation\n\" '$ ' -> escaped space\n\" $simple_varname -> variable\n\" ${varname} -> variable\n\nsyn match   ninjaDollar \"\\$\\$\"\nsyn match   ninjaWrapLineOperator \"\\$$\"\nsyn match   ninjaSimpleVar \"\\$[a-zA-Z0-9_-]\\+\"\nsyn match   ninjaVar       \"\\${[a-zA-Z0-9_.-]\\+}\"\n\n\" operators are:\n\" variable assignment =\n\" rule definition :\n\" implicit dependency |\n\" order-only dependency ||\nsyn match ninjaOperator \"\\(=\\|:\\||\\|||\\)\\ze\\s\"\n\nhi def link ninjaComment Comment\nhi def link ninjaKeyword Keyword\nhi def link ninjaRuleCommand Statement\nhi def link ninjaPoolCommand Statement\nhi def link ninjaDollar ninjaOperator\nhi def link ninjaWrapLineOperator ninjaOperator\nhi def link ninjaOperator Operator\nhi def link ninjaSimpleVar ninjaVar\nhi def link ninjaVar Identifier\n\nlet b:current_syntax = \"ninja\"\n\nlet &cpo = s:cpo_save\nunlet s:cpo_save\n"
  },
  {
    "path": "misc/ninja_syntax.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2011 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Python module for generating .ninja files.\n\nNote that this is emphatically not a required piece of Ninja; it's\njust a helpful utility for build-file-generation systems that already\nuse Python.\n\"\"\"\n\nimport re\nimport textwrap\nfrom io import TextIOWrapper\nfrom typing import Dict, List, Match, Optional, Tuple, Union\n\ndef escape_path(word: str) -> str:\n    return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:')\n\nclass Writer(object):\n    def __init__(self, output: TextIOWrapper, width: int = 78) -> None:\n        self.output = output\n        self.width = width\n\n    def newline(self) -> None:\n        self.output.write('\\n')\n\n    def comment(self, text: str) -> None:\n        for line in textwrap.wrap(text, self.width - 2, break_long_words=False,\n                                  break_on_hyphens=False):\n            self.output.write('# ' + line + '\\n')\n\n    def variable(\n        self,\n        key: str,\n        value: Optional[Union[bool, int, float, str, List[str]]],\n        indent: int = 0,\n    ) -> None:\n        if value is None:\n            return\n        if isinstance(value, list):\n            value = ' '.join(filter(None, value))  # Filter out empty strings.\n        self._line('%s = %s' % (key, value), indent)\n\n    def pool(self, name: str, depth: int) -> None:\n        self._line('pool %s' % name)\n        self.variable('depth', depth, indent=1)\n\n    def rule(\n        self,\n        name: str,\n        command: str,\n        description: Optional[str] = None,\n        depfile: Optional[str] = None,\n        generator: bool = False,\n        pool: Optional[str] = None,\n        restat: bool = False,\n        rspfile: Optional[str] = None,\n        rspfile_content: Optional[str] = None,\n        deps: Optional[Union[str, List[str]]] = None,\n    ) -> None:\n        self._line('rule %s' % name)\n        self.variable('command', command, indent=1)\n        if description:\n            self.variable('description', description, indent=1)\n        if depfile:\n            self.variable('depfile', depfile, indent=1)\n        if generator:\n            self.variable('generator', '1', indent=1)\n        if pool:\n            self.variable('pool', pool, indent=1)\n        if restat:\n            self.variable('restat', '1', indent=1)\n        if rspfile:\n            self.variable('rspfile', rspfile, indent=1)\n        if rspfile_content:\n            self.variable('rspfile_content', rspfile_content, indent=1)\n        if deps:\n            self.variable('deps', deps, indent=1)\n\n    def build(\n        self,\n        outputs: Union[str, List[str]],\n        rule: str,\n        inputs: Optional[Union[str, List[str]]] = None,\n        implicit: Optional[Union[str, List[str]]] = None,\n        order_only: Optional[Union[str, List[str]]] = None,\n        variables: Optional[\n            Union[\n                List[Tuple[str, Optional[Union[str, List[str]]]]],\n                Dict[str, Optional[Union[str, List[str]]]],\n            ]\n        ] = None,\n        implicit_outputs: Optional[Union[str, List[str]]] = None,\n        pool: Optional[str] = None,\n        dyndep: Optional[str] = None,\n    ) -> List[str]:\n        outputs = as_list(outputs)\n        out_outputs = [escape_path(x) for x in outputs]\n        all_inputs = [escape_path(x) for x in as_list(inputs)]\n\n        if implicit:\n            implicit = [escape_path(x) for x in as_list(implicit)]\n            all_inputs.append('|')\n            all_inputs.extend(implicit)\n        if order_only:\n            order_only = [escape_path(x) for x in as_list(order_only)]\n            all_inputs.append('||')\n            all_inputs.extend(order_only)\n        if implicit_outputs:\n            implicit_outputs = [escape_path(x)\n                                for x in as_list(implicit_outputs)]\n            out_outputs.append('|')\n            out_outputs.extend(implicit_outputs)\n\n        self._line('build %s: %s' % (' '.join(out_outputs),\n                                     ' '.join([rule] + all_inputs)))\n        if pool is not None:\n            self._line('  pool = %s' % pool)\n        if dyndep is not None:\n            self._line('  dyndep = %s' % dyndep)\n\n        if variables:\n            if isinstance(variables, dict):\n                iterator = iter(variables.items())\n            else:\n                iterator = iter(variables)\n\n            for key, val in iterator:\n                self.variable(key, val, indent=1)\n\n        return outputs\n\n    def include(self, path: str) -> None:\n        self._line('include %s' % path)\n\n    def subninja(self, path: str) -> None:\n        self._line('subninja %s' % path)\n\n    def default(self, paths: Union[str, List[str]]) -> None:\n        self._line('default %s' % ' '.join(as_list(paths)))\n\n    def _count_dollars_before_index(self, s: str, i: int) -> int:\n        \"\"\"Returns the number of '$' characters right in front of s[i].\"\"\"\n        dollar_count = 0\n        dollar_index = i - 1\n        while dollar_index > 0 and s[dollar_index] == '$':\n            dollar_count += 1\n            dollar_index -= 1\n        return dollar_count\n\n    def _line(self, text: str, indent: int = 0) -> None:\n        \"\"\"Write 'text' word-wrapped at self.width characters.\"\"\"\n        leading_space = '  ' * indent\n        while len(leading_space) + len(text) > self.width:\n            # The text is too wide; wrap if possible.\n\n            # Find the rightmost space that would obey our width constraint and\n            # that's not an escaped space.\n            available_space = self.width - len(leading_space) - len(' $')\n            space = available_space\n            while True:\n                space = text.rfind(' ', 0, space)\n                if (space < 0 or\n                    self._count_dollars_before_index(text, space) % 2 == 0):\n                    break\n\n            if space < 0:\n                # No such space; just use the first unescaped space we can find.\n                space = available_space - 1\n                while True:\n                    space = text.find(' ', space + 1)\n                    if (space < 0 or\n                        self._count_dollars_before_index(text, space) % 2 == 0):\n                        break\n            if space < 0:\n                # Give up on breaking.\n                break\n\n            self.output.write(leading_space + text[0:space] + ' $\\n')\n            text = text[space+1:]\n\n            # Subsequent lines are continuations, so indent them.\n            leading_space = '  ' * (indent+2)\n\n        self.output.write(leading_space + text + '\\n')\n\n    def close(self) -> None:\n        self.output.close()\n\n\ndef as_list(input: Optional[Union[str, List[str]]]) -> List[str]:\n    if input is None:\n        return []\n    if isinstance(input, list):\n        return input\n    return [input]\n\n\ndef escape(string: str) -> str:\n    \"\"\"Escape a string such that it can be embedded into a Ninja file without\n    further interpretation.\"\"\"\n    assert '\\n' not in string, 'Ninja syntax does not allow newlines'\n    # We only have one special metacharacter: '$'.\n    return string.replace('$', '$$')\n\n\ndef expand(string: str, vars: Dict[str, str], local_vars: Dict[str, str] = {}) -> str:\n    \"\"\"Expand a string containing $vars as Ninja would.\n\n    Note: doesn't handle the full Ninja variable syntax, but it's enough\n    to make configure.py's use of it work.\n    \"\"\"\n    def exp(m: Match[str]) -> str:\n        var = m.group(1)\n        if var == '$':\n            return '$'\n        return local_vars.get(var, vars.get(var, ''))\n    return re.sub(r'\\$(\\$|\\w*)', exp, string)\n"
  },
  {
    "path": "misc/ninja_syntax_test.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2011 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport unittest\nfrom typing import Dict\n\ntry:\n    from StringIO import StringIO\nexcept ImportError:\n    from io import StringIO\n\nimport ninja_syntax\n\nLONGWORD = 'a' * 10\nLONGWORDWITHSPACES = 'a'*5 + '$ ' + 'a'*5\nINDENT = '    '\n\nclass TestLineWordWrap(unittest.TestCase):\n    def setUp(self) -> None:\n        self.out = StringIO()\n        self.n = ninja_syntax.Writer(self.out, width=8)\n\n    def test_single_long_word(self) -> None:\n        # We shouldn't wrap a single long word.\n        self.n._line(LONGWORD)\n        self.assertEqual(LONGWORD + '\\n', self.out.getvalue())\n\n    def test_few_long_words(self) -> None:\n        # We should wrap a line where the second word is overlong.\n        self.n._line(' '.join(['x', LONGWORD, 'y']))\n        self.assertEqual(' $\\n'.join(['x',\n                                      INDENT + LONGWORD,\n                                      INDENT + 'y']) + '\\n',\n                         self.out.getvalue())\n\n    def test_comment_wrap(self) -> None:\n        # Filenames should not be wrapped\n        self.n.comment('Hello /usr/local/build-tools/bin')\n        self.assertEqual('# Hello\\n# /usr/local/build-tools/bin\\n',\n                         self.out.getvalue())\n\n    def test_short_words_indented(self) -> None:\n        # Test that indent is taking into account when breaking subsequent lines.\n        # The second line should not be '    to tree', as that's longer than the\n        # test layout width of 8.\n        self.n._line('line_one to tree')\n        self.assertEqual('''\\\nline_one $\n    to $\n    tree\n''',\n                         self.out.getvalue())\n\n    def test_few_long_words_indented(self) -> None:\n        # Check wrapping in the presence of indenting.\n        self.n._line(' '.join(['x', LONGWORD, 'y']), indent=1)\n        self.assertEqual(' $\\n'.join(['  ' + 'x',\n                                      '  ' + INDENT + LONGWORD,\n                                      '  ' + INDENT + 'y']) + '\\n',\n                         self.out.getvalue())\n\n    def test_escaped_spaces(self) -> None:\n        self.n._line(' '.join(['x', LONGWORDWITHSPACES, 'y']))\n        self.assertEqual(' $\\n'.join(['x',\n                                      INDENT + LONGWORDWITHSPACES,\n                                      INDENT + 'y']) + '\\n',\n                         self.out.getvalue())\n\n    def test_fit_many_words(self) -> None:\n        self.n = ninja_syntax.Writer(self.out, width=78)\n        self.n._line('command = cd ../../chrome; python ../tools/grit/grit/format/repack.py ../out/Debug/obj/chrome/chrome_dll.gen/repack/theme_resources_large.pak ../out/Debug/gen/chrome/theme_resources_large.pak', 1)\n        self.assertEqual('''\\\n  command = cd ../../chrome; python ../tools/grit/grit/format/repack.py $\n      ../out/Debug/obj/chrome/chrome_dll.gen/repack/theme_resources_large.pak $\n      ../out/Debug/gen/chrome/theme_resources_large.pak\n''',\n                         self.out.getvalue())\n\n    def test_leading_space(self) -> None:\n        self.n = ninja_syntax.Writer(self.out, width=14)  # force wrapping\n        self.n.variable('foo', ['', '-bar', '-somethinglong'], 0)\n        self.assertEqual('''\\\nfoo = -bar $\n    -somethinglong\n''',\n                         self.out.getvalue())\n\n    def test_embedded_dollar_dollar(self) -> None:\n        self.n = ninja_syntax.Writer(self.out, width=15)  # force wrapping\n        self.n.variable('foo', ['a$$b', '-somethinglong'], 0)\n        self.assertEqual('''\\\nfoo = a$$b $\n    -somethinglong\n''',\n                         self.out.getvalue())\n\n    def test_two_embedded_dollar_dollars(self) -> None:\n        self.n = ninja_syntax.Writer(self.out, width=17)  # force wrapping\n        self.n.variable('foo', ['a$$b', '-somethinglong'], 0)\n        self.assertEqual('''\\\nfoo = a$$b $\n    -somethinglong\n''',\n                         self.out.getvalue())\n\n    def test_leading_dollar_dollar(self) -> None:\n        self.n = ninja_syntax.Writer(self.out, width=14)  # force wrapping\n        self.n.variable('foo', ['$$b', '-somethinglong'], 0)\n        self.assertEqual('''\\\nfoo = $$b $\n    -somethinglong\n''',\n                         self.out.getvalue())\n\n    def test_trailing_dollar_dollar(self) -> None:\n        self.n = ninja_syntax.Writer(self.out, width=14)  # force wrapping\n        self.n.variable('foo', ['a$$', '-somethinglong'], 0)\n        self.assertEqual('''\\\nfoo = a$$ $\n    -somethinglong\n''',\n                         self.out.getvalue())\n\nclass TestBuild(unittest.TestCase):\n    def setUp(self) -> None:\n        self.out = StringIO()\n        self.n = ninja_syntax.Writer(self.out)\n\n    def test_variables_dict(self) -> None:\n        self.n.build('out', 'cc', 'in', variables={'name': 'value'})\n        self.assertEqual('''\\\nbuild out: cc in\n  name = value\n''',\n                         self.out.getvalue())\n\n    def test_variables_list(self) -> None:\n        self.n.build('out', 'cc', 'in', variables=[('name', 'value')])\n        self.assertEqual('''\\\nbuild out: cc in\n  name = value\n''',\n                         self.out.getvalue())\n\n    def test_implicit_outputs(self) -> None:\n        self.n.build('o', 'cc', 'i', implicit_outputs='io')\n        self.assertEqual('''\\\nbuild o | io: cc i\n''',\n                         self.out.getvalue())\n\nclass TestExpand(unittest.TestCase):\n    def test_basic(self) -> None:\n        vars = {'x': 'X'}\n        self.assertEqual('foo', ninja_syntax.expand('foo', vars))\n\n    def test_var(self) -> None:\n        vars = {'xyz': 'XYZ'}\n        self.assertEqual('fooXYZ', ninja_syntax.expand('foo$xyz', vars))\n\n    def test_vars(self) -> None:\n        vars = {'x': 'X', 'y': 'YYY'}\n        self.assertEqual('XYYY', ninja_syntax.expand('$x$y', vars))\n\n    def test_space(self) -> None:\n        vars: Dict[str, str] = {}\n        self.assertEqual('x y z', ninja_syntax.expand('x$ y$ z', vars))\n\n    def test_locals(self) -> None:\n        vars = {'x': 'a'}\n        local_vars = {'x': 'b'}\n        self.assertEqual('a', ninja_syntax.expand('$x', vars))\n        self.assertEqual('b', ninja_syntax.expand('$x', vars, local_vars))\n\n    def test_double(self) -> None:\n        self.assertEqual('a b$c', ninja_syntax.expand('a$ b$$c', {}))\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "misc/oss-fuzz/build.sh",
    "content": "#!/bin/bash -eu\n# Copyright 2020 Google Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n################################################################################\n\ncmake -Bbuild-cmake -H.\ncmake --build build-cmake\n\ncd $SRC/ninja/misc\n\n$CXX $CXXFLAGS -fdiagnostics-color -I/src/ninja/src -o fuzzer.o -c manifest_fuzzer.cc\n\nfind .. -name \"*.o\" -exec ar rcs fuzz_lib.a {} \\;\n\n$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzzer.o -o $OUT/fuzzer fuzz_lib.a\n\nzip $OUT/fuzzer_seed_corpus.zip $SRC/sample_ninja_build\n"
  },
  {
    "path": "misc/oss-fuzz/sample_ninja_build",
    "content": "# build.ninja\ncc     = clang\ncflags = -Weverything\n\nrule compile\n  command = $cc $cflags -c $in -o $out\n\nrule link\n  command = $cc $in -o $out\n\nbuild hello.o: compile hello.c\nbuild hello: link hello.o\n\ndefault hello\n"
  },
  {
    "path": "misc/output_test.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"Runs ./ninja and checks if the output is correct.\n\nIn order to simulate a smart terminal it uses the 'script' command.\n\"\"\"\n\nimport os\nimport platform\nimport signal\nimport subprocess\nimport sys\nimport tempfile\nimport time\nimport unittest\nfrom textwrap import dedent\nimport typing as T\n\ndefault_env = dict(os.environ)\ndefault_env.pop('NINJA_STATUS', None)\ndefault_env.pop('CLICOLOR_FORCE', None)\ndefault_env['TERM'] = ''\nNINJA_PATH = os.path.abspath('./ninja')\n\ndef remove_non_visible_lines(raw_output: bytes) -> str:\n  # When running in a smart terminal, Ninja uses CR (\\r) to\n  # return the cursor to the start of the current line, prints\n  # something, then uses `\\x1b[K` to clear everything until\n  # the end of the line.\n  #\n  # Thus printing 'FOO', 'BAR', 'ZOO' on the same line, then\n  # jumping to the next one results in the following output\n  # on Posix:\n  #\n  # '\\rFOO\\x1b[K\\rBAR\\x1b[K\\rZOO\\x1b[K\\r\\n'\n  #\n  # The following splits the output at both \\r, \\n and \\r\\n\n  # boundaries, which gives:\n  #\n  #  [ '\\r', 'FOO\\x1b[K\\r', 'BAR\\x1b[K\\r', 'ZOO\\x1b[K\\r\\n' ]\n  #\n  decoded_lines = raw_output.decode('utf-8').splitlines(True)\n\n  # Remove any item that ends with a '\\r' as this means its\n  # content will be overwritten by the next item in the list.\n  # For the previous example, this gives:\n  #\n  #  [ 'ZOO\\x1b[K\\r\\n' ]\n  #\n  final_lines = [ l for l in decoded_lines if not l.endswith('\\r') ]\n\n  # Return a single string that concatenates all filtered lines\n  # while removing any remaining \\r in it. Needed to transform\n  # \\r\\n into \\n.\n  #\n  #  \"ZOO\\x1b[K\\n'\n  #\n  return ''.join(final_lines).replace('\\r', '')\n\nclass BuildDir:\n    def __init__(self, build_ninja: str):\n        self.build_ninja = dedent(build_ninja)\n        self.d = None\n\n    def __enter__(self):\n        self.d = tempfile.TemporaryDirectory()\n        with open(os.path.join(self.d.name, 'build.ninja'), 'w') as f:\n            f.write(self.build_ninja)\n            f.flush()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.d.cleanup()\n\n    @property\n    def path(self) -> str:\n        return os.path.realpath(self.d.name)\n\n\n    def run(\n        self,\n        flags: T.Optional[str] = None,\n        pipe: bool = False,\n        raw_output: bool = False,\n        env: T.Dict[str, str] = default_env,\n        print_err_output = True,\n    ) -> str:\n        \"\"\"Run Ninja command, and get filtered output.\n\n        Args:\n          flags: Extra arguments passed to Ninja.\n\n          pipe: set to True to run Ninja in a non-interactive terminal.\n            If False (the default), this runs Ninja in a pty to simulate\n            a smart terminal (this feature cannot work on Windows!).\n\n          raw_output: set to True to return the raw, unfiltered command\n            output.\n\n          env: Optional environment dictionary to run the command in.\n\n          print_err_output: set to False if the test expects ninja to print\n            something to stderr. (Otherwise, an error message from Ninja\n            probably represents a failed test.)\n\n        Returns:\n          A UTF-8 string corresponding to the output (stdout only) of the\n          Ninja command. By default, partial lines that were overwritten\n          are removed according to the rules described in the comments\n          below.\n        \"\"\"\n        ninja_cmd = '{} {}'.format(NINJA_PATH, flags if flags else '')\n        try:\n            if pipe:\n                output = subprocess.check_output(\n                    [ninja_cmd], shell=True, cwd=self.d.name, env=env)\n            elif platform.system() == 'Darwin':\n                output = subprocess.check_output(['script', '-q', '/dev/null', 'bash', '-c', ninja_cmd],\n                                                 cwd=self.d.name, env=env)\n            else:\n                output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'],\n                                                 cwd=self.d.name, env=env)\n        except subprocess.CalledProcessError as err:\n            if print_err_output:\n              sys.stdout.buffer.write(err.output)\n            err.cooked_output = remove_non_visible_lines(err.output)\n            raise err\n\n        if raw_output:\n            return output.decode('utf-8')\n        return remove_non_visible_lines(output)\n\ndef run(\n    build_ninja: str,\n    flags: T.Optional[str] = None,\n    pipe: bool = False,\n    raw_output: bool = False,\n    env: T.Dict[str, str] = default_env,\n    print_err_output = True,\n) -> str:\n    \"\"\"Run Ninja with a given build plan in a temporary directory.\n    \"\"\"\n    with BuildDir(build_ninja) as b:\n        return b.run(flags, pipe, raw_output, env, print_err_output)\n\n@unittest.skipIf(platform.system() == 'Windows', 'These test methods do not work on Windows')\nclass Output(unittest.TestCase):\n    BUILD_SIMPLE_ECHO = '\\n'.join((\n        'rule echo',\n        '  command = printf \"do thing\"',\n        '  description = echo $out',\n        '',\n        'build a: echo',\n        '',\n    ))\n\n    def _test_expected_error(self, plan: str, flags: T.Optional[str],expected: str,\n                             *args, exit_code: T.Optional[int]=None, **kwargs)->None:\n        \"\"\"Run Ninja with a given plan and flags, and verify its cooked output against an expected content.\n        All *args and **kwargs are passed to the `run` function\n        \"\"\"\n        actual = ''\n        kwargs['print_err_output'] = False\n        with self.assertRaises(subprocess.CalledProcessError) as cm:\n            run(plan, flags, *args,  **kwargs)\n        actual = cm.exception.cooked_output\n        if exit_code is not None:\n            self.assertEqual(cm.exception.returncode, exit_code)\n        self.assertEqual(expected, actual)\n\n    def test_issue_1418(self) -> None:\n        self.assertEqual(run(\n'''rule echo\n  command = sleep $delay && echo $out\n  description = echo $out\n\nbuild a: echo\n  delay = 3\nbuild b: echo\n  delay = 2\nbuild c: echo\n  delay = 1\n''', '-j3'),\n'''[1/3] echo c\\x1b[K\nc\n[2/3] echo b\\x1b[K\nb\n[3/3] echo a\\x1b[K\na\n''')\n\n    def test_issue_1214(self) -> None:\n        print_red = '''rule echo\n  command = printf '\\x1b[31mred\\x1b[0m'\n  description = echo $out\n\nbuild a: echo\n'''\n        # Only strip color when ninja's output is piped.\n        self.assertEqual(run(print_red),\n'''[1/1] echo a\\x1b[K\n\\x1b[31mred\\x1b[0m\n''')\n        self.assertEqual(run(print_red, pipe=True),\n'''[1/1] echo a\nred\n''')\n        # Even in verbose mode, colors should still only be stripped when piped.\n        self.assertEqual(run(print_red, flags='-v'),\n'''[1/1] printf '\\x1b[31mred\\x1b[0m'\n\\x1b[31mred\\x1b[0m\n''')\n        self.assertEqual(run(print_red, flags='-v', pipe=True),\n'''[1/1] printf '\\x1b[31mred\\x1b[0m'\nred\n''')\n\n        # CLICOLOR_FORCE=1 can be used to disable escape code stripping.\n        env = default_env.copy()\n        env['CLICOLOR_FORCE'] = '1'\n        self.assertEqual(run(print_red, pipe=True, env=env),\n'''[1/1] echo a\n\\x1b[31mred\\x1b[0m\n''')\n\n    def test_issue_1966(self) -> None:\n        self.assertEqual(run(\n'''rule cat\n  command = cat $rspfile $rspfile > $out\n  rspfile = cat.rsp\n  rspfile_content = a b c\n\nbuild a: cat\n''', '-j3'),\n'''[1/1] cat cat.rsp cat.rsp > a\\x1b[K\n''')\n\n    def test_issue_2499(self) -> None:\n        # This verifies that Ninja prints its status line updates on a single\n        # line when running in a smart terminal, and when commands do not have\n        # any output. Get the raw command output which includes CR (\\r) codes\n        # and all content that was printed by Ninja.\n        self.assertEqual(run(\n'''rule touch\n  command = touch $out\n\nbuild foo: touch\nbuild bar: touch foo\nbuild zoo: touch bar\n''', flags='-j1 zoo', raw_output=True).split('\\r'),\n            [\n                '',\n                '[0/3] touch foo\\x1b[K',\n                '[1/3] touch foo\\x1b[K',\n                '[1/3] touch bar\\x1b[K',\n                '[2/3] touch bar\\x1b[K',\n                '[2/3] touch zoo\\x1b[K',\n                '[3/3] touch zoo\\x1b[K',\n                '\\n',\n            ])\n\n    def test_pr_1685(self) -> None:\n        # Running those tools without .ninja_deps and .ninja_log shouldn't fail.\n        self.assertEqual(run('', flags='-t recompact'), '')\n        self.assertEqual(run('', flags='-t restat'), '')\n\n    def test_issue_2048(self) -> None:\n        with tempfile.TemporaryDirectory() as d:\n            with open(os.path.join(d, 'build.ninja'), 'w'):\n                pass\n\n            with open(os.path.join(d, '.ninja_log'), 'w') as f:\n                f.write('# ninja log v4\\n')\n\n            try:\n                output = subprocess.check_output([NINJA_PATH, '-t', 'recompact'],\n                                                 cwd=d,\n                                                 env=default_env,\n                                                 stderr=subprocess.STDOUT,\n                                                 text=True\n                                                 )\n\n                self.assertEqual(\n                    output.strip(),\n                    \"ninja: warning: build log version is too old; starting over\"\n                )\n            except subprocess.CalledProcessError as err:\n                self.fail(\"non-zero exit code with: \" + err.output)\n\n    def test_pr_2540(self)->None:\n        py = sys.executable\n        plan = f'''\\\nrule CUSTOM_COMMAND\n  command = $COMMAND\n\nbuild 124: CUSTOM_COMMAND\n  COMMAND = {py} -c 'exit(124)'\n\nbuild 127: CUSTOM_COMMAND\n  COMMAND = {py} -c 'exit(127)'\n\nbuild 130: CUSTOM_COMMAND\n  COMMAND = {py} -c 'exit(130)'\n\nbuild 137: CUSTOM_COMMAND\n  COMMAND = {py} -c 'exit(137)'\n\nbuild success: CUSTOM_COMMAND\n  COMMAND = sleep 0.3; echo success\n'''\n        # Disable colors\n        env = default_env.copy()\n        env['TERM'] = 'dumb'\n        self._test_expected_error(\n            plan, '124',\n            f'''[1/1] {py} -c 'exit(124)'\nFAILED: [code=124] 124 \\n{py} -c 'exit(124)'\nninja: build stopped: subcommand failed.\n''',\n            exit_code=124, env=env,\n        )\n        self._test_expected_error(\n            plan, '127',\n            f'''[1/1] {py} -c 'exit(127)'\nFAILED: [code=127] 127 \\n{py} -c 'exit(127)'\nninja: build stopped: subcommand failed.\n''',\n            exit_code=127, env=env,\n        )\n        self._test_expected_error(\n            plan, '130',\n            'ninja: build stopped: interrupted by user.\\n',\n            exit_code=130, env=env,\n        )\n        self._test_expected_error(\n            plan, '137',\n            f'''[1/1] {py} -c 'exit(137)'\nFAILED: [code=137] 137 \\n{py} -c 'exit(137)'\nninja: build stopped: subcommand failed.\n''',\n            exit_code=137, env=env,\n        )\n        self._test_expected_error(\n            plan, 'non-existent-target',\n            \"ninja: error: unknown target 'non-existent-target'\\n\",\n            exit_code=1, env=env,\n        )\n        self._test_expected_error(\n            plan, '-j2 success 127',\n            f'''[1/2] {py} -c 'exit(127)'\nFAILED: [code=127] 127 \\n{py} -c 'exit(127)'\n[2/2] sleep 0.3; echo success\nsuccess\nninja: build stopped: subcommand failed.\n''',\n            exit_code=127, env=env,\n        )\n\n    def test_depfile_directory_creation(self) -> None:\n        b = BuildDir('''\\\n            rule touch\n              command = touch $out && echo \"$out: extra\" > $depfile\n\n            build somewhere/out: touch\n              depfile = somewhere_else/out.d\n            ''')\n        with b:\n            self.assertEqual(b.run('', pipe=True), dedent('''\\\n                [1/1] touch somewhere/out && echo \"somewhere/out: extra\" > somewhere_else/out.d\n                '''))\n            self.assertTrue(os.path.isfile(os.path.join(b.d.name, \"somewhere\", \"out\")))\n            self.assertTrue(os.path.isfile(os.path.join(b.d.name, \"somewhere_else\", \"out.d\")))\n\n    def test_status(self) -> None:\n        self.assertEqual(run(''), 'ninja: no work to do.\\n')\n        self.assertEqual(run('', pipe=True), 'ninja: no work to do.\\n')\n        self.assertEqual(run('', flags='--quiet'), '')\n\n    def test_ninja_status_default(self) -> None:\n        'Do we show the default status by default?'\n        self.assertEqual(run(Output.BUILD_SIMPLE_ECHO), '[1/1] echo a\\x1b[K\\ndo thing\\n')\n\n    def test_ninja_status_quiet(self) -> None:\n        'Do we suppress the status information when --quiet is specified?'\n        output = run(Output.BUILD_SIMPLE_ECHO, flags='--quiet')\n        self.assertEqual(output, 'do thing\\n')\n\n    def test_entering_directory_on_stdout(self) -> None:\n        output = run(Output.BUILD_SIMPLE_ECHO, flags='-C$PWD', pipe=True)\n        self.assertEqual(output.splitlines()[0][:25], \"ninja: Entering directory\")\n\n    def test_tool_inputs(self) -> None:\n        plan = '''\nrule cat\n  command = cat $in $out\nbuild out1 : cat in1\nbuild out2 : cat in2 out1\nbuild out3 : cat out2 out1 | implicit || order_only\n'''\n        self.assertEqual(run(plan, flags='-t inputs out3'),\n'''implicit\nin1\nin2\norder_only\nout1\nout2\n''')\n\n        self.assertEqual(run(plan, flags='-t inputs --dependency-order out3'),\n'''in2\nin1\nout1\nout2\nimplicit\norder_only\n''')\n\n        # Verify that results are shell-escaped by default, unless --no-shell-escape\n        # is used. Also verify that phony outputs are never part of the results.\n        quote = '\"' if platform.system() == \"Windows\" else \"'\"\n\n        plan = '''\nrule cat\n  command = cat $in $out\nbuild out1 : cat in1\nbuild out$ 2 : cat out1\nbuild out$ 3 : phony out$ 2\nbuild all: phony out$ 3\n'''\n\n        # Quoting changes the order of results when sorting alphabetically.\n        self.assertEqual(run(plan, flags='-t inputs all'),\nf'''{quote}out 2{quote}\nin1\nout1\n''')\n\n        self.assertEqual(run(plan, flags='-t inputs --no-shell-escape all'),\n'''in1\nout 2\nout1\n''')\n\n        # But not when doing dependency order.\n        self.assertEqual(\n            run(\n              plan,\n              flags='-t inputs --dependency-order all'\n            ),\n            f'''in1\nout1\n{quote}out 2{quote}\n''')\n\n        self.assertEqual(\n          run(\n            plan,\n            flags='-t inputs --dependency-order --no-shell-escape all'\n          ),\n          f'''in1\nout1\nout 2\n''')\n\n        self.assertEqual(\n          run(\n            plan,\n            flags='-t inputs --dependency-order --no-shell-escape --print0 all'\n          ),\n          f'''in1\\0out1\\0out 2\\0'''\n        )\n\n\n    def test_tool_compdb_targets(self) -> None:\n        plan = '''\nrule cat\n  command = cat $in $out\nbuild out1 : cat in1\nbuild out2 : cat in2 out1\nbuild out3 : cat out2 out1\nbuild out4 : cat in4\n'''\n\n\n        self._test_expected_error(plan, '-t compdb-targets',\n'''ninja: error: compdb-targets expects the name of at least one target\nusage: ninja -t compdb [-hx] target [targets]\n\noptions:\n  -h     display this help message\n  -x     expand @rspfile style response file invocations\n''')\n\n        self._test_expected_error(plan, '-t compdb-targets in1',\n            \"ninja: fatal: 'in1' is not a target (i.e. it is not an output of any `build` statement)\\n\")\n\n        self._test_expected_error(plan, '-t compdb-targets nonexistent_target',\n            \"ninja: fatal: unknown target 'nonexistent_target'\\n\")\n\n\n        with BuildDir(plan) as b:\n            actual = b.run(flags='-t compdb-targets out3')\n            expected = f'''[\n  {{\n    \"directory\": \"{b.path}\",\n    \"command\": \"cat in1 out1\",\n    \"file\": \"in1\",\n    \"output\": \"out1\"\n  }},\n  {{\n    \"directory\": \"{b.path}\",\n    \"command\": \"cat in2 out1 out2\",\n    \"file\": \"in2\",\n    \"output\": \"out2\"\n  }},\n  {{\n    \"directory\": \"{b.path}\",\n    \"command\": \"cat in2 out1 out2\",\n    \"file\": \"out1\",\n    \"output\": \"out2\"\n  }},\n  {{\n    \"directory\": \"{b.path}\",\n    \"command\": \"cat out2 out1 out3\",\n    \"file\": \"out2\",\n    \"output\": \"out3\"\n  }},\n  {{\n    \"directory\": \"{b.path}\",\n    \"command\": \"cat out2 out1 out3\",\n    \"file\": \"out1\",\n    \"output\": \"out3\"\n  }}\n]\n'''\n            self.assertEqual(expected, actual)\n\n\n    def test_tool_multi_inputs(self) -> None:\n        plan = '''\nrule cat\n  command = cat $in $out\nbuild out1 : cat in1\nbuild out2 : cat in1 in2\nbuild out3 : cat in1 in2 in3\n'''\n        self.assertEqual(run(plan, flags='-t multi-inputs out1'),\n'''out1<TAB>in1\n'''.replace(\"<TAB>\", \"\\t\"))\n\n        self.assertEqual(run(plan, flags='-t multi-inputs out1 out2 out3'),\n'''out1<TAB>in1\nout2<TAB>in1\nout2<TAB>in2\nout3<TAB>in1\nout3<TAB>in2\nout3<TAB>in3\n'''.replace(\"<TAB>\", \"\\t\"))\n\n        self.assertEqual(run(plan, flags='-t multi-inputs -d: out1'),\n'''out1:in1\n''')\n\n        self.assertEqual(\n          run(\n            plan,\n            flags='-t multi-inputs -d, --print0 out1 out2'\n          ),\n          '''out1,in1\\0out2,in1\\0out2,in2\\0'''\n        )\n\n\n    def test_explain_output(self):\n        b = BuildDir('''\\\n            build .FORCE: phony\n            rule create_if_non_exist\n              command = [ -e $out ] || touch $out\n              restat = true\n            rule write\n              command = cp $in $out\n            build input : create_if_non_exist .FORCE\n            build mid : write input\n            build output : write mid\n            default output\n            ''')\n        with b:\n            # The explain output is shown just before the relevant build:\n            self.assertEqual(b.run('-v -d explain'), dedent('''\\\n                ninja explain: .FORCE is dirty\n                [1/3] [ -e input ] || touch input\n                ninja explain: input is dirty\n                [2/3] cp input mid\n                ninja explain: mid is dirty\n                [3/3] cp mid output\n                '''))\n            # Don't print \"ninja explain: XXX is dirty\" for inputs that are\n            # pruned from the graph by an earlier restat.\n            self.assertEqual(b.run('-v -d explain'), dedent('''\\\n                ninja explain: .FORCE is dirty\n                [1/3] [ -e input ] || touch input\n                '''))\n\n    def test_issue_2586(self):\n        \"\"\"This shouldn't hang\"\"\"\n        plan = '''rule echo\n  command = echo echo\nbuild dep: echo\nbuild console1: echo dep\n  pool = console\nbuild console2: echo\n  pool = console\nbuild all: phony console1 console2\ndefault all\n'''\n        self.assertEqual(run(plan, flags='-j2', env={'NINJA_STATUS':''}), '''echo echo\necho\necho echo\necho\necho echo\necho\n''')\n\n    def test_issue_2621(self):\n        \"\"\"Should result in \"multiple rules generate\" error\"\"\"\n        plan = r\"\"\"rule dd\n  command = printf 'ninja_dyndep_version = 1\\nbuild stamp-$n | out: dyndep\\n' > $out\nrule touch\n  command = touch stamp-$n out\n  dyndep = dd-$n\nbuild dd-1: dd\n  n = 1\nbuild dd-2: dd\n  n = 2\nbuild stamp-1: touch || dd-1\n  n = 1\nbuild stamp-2: touch || dd-2\n  n = 2\n\"\"\"\n        self._test_expected_error(\n            plan,\n            \"-v\",\n            r\"\"\"[1/4] printf 'ninja_dyndep_version = 1\\nbuild stamp-1 | out: dyndep\\n' > dd-1\n[2/4] printf 'ninja_dyndep_version = 1\\nbuild stamp-2 | out: dyndep\\n' > dd-2\nninja: build stopped: multiple rules generate out.\n\"\"\",\n        )\n\n    def test_issue_2681(self):\n        \"\"\"Ninja should return a status code of 130 when interrupted.\"\"\"\n        plan = r\"\"\"rule sleep\n  command = sleep 10\n\nbuild foo: sleep\n\"\"\"\n        with BuildDir(plan) as b:\n            for signum in (signal.SIGINT, signal.SIGHUP, signal.SIGTERM):\n                proc = subprocess.Popen([NINJA_PATH, \"foo\"], cwd=b.path, env=default_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n                # Sleep a bit to let Ninja start the build, otherwise the signal could be received\n                # before it, and returncode will be -2.\n                time.sleep(0.2)\n                os.kill(proc.pid, signum)\n                proc.wait()\n                self.assertEqual(proc.returncode, 130, msg=f\"For signal {signum}\")\n\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "misc/packaging/ninja.spec",
    "content": "Summary: Ninja is a small build system with a focus on speed.\nName: ninja\nVersion: %{ver}\nRelease: %{rel}%{?dist}\nGroup: Development/Tools\nLicense: Apache 2.0\nURL: https://github.com/ninja-build/ninja\nSource0: %{name}-%{version}-%{rel}.tar.gz\nBuildRoot: %{_tmppath}/%{name}-%{version}-%{rel}\n\nBuildRequires: asciidoc\n\n%description\nNinja is yet another build system. It takes as input the interdependencies of files (typically source code and output executables) and\norchestrates building them, quickly.\n\nNinja 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,\nwhich has over 30,000 source files and whose other build systems (including one built from custom non-recursive Makefiles) can take ten\nseconds to start building after changing one file. Ninja is under a second.\n\n%prep\n%setup -q -n %{name}-%{version}-%{rel}\n\n%build\necho Building..\n./configure.py --bootstrap\n./ninja manual\n\n%install\nmkdir -p %{buildroot}%{_bindir} %{buildroot}%{_docdir}\ncp -p ninja %{buildroot}%{_bindir}/\n\n%files\n%defattr(-, root, root)\n%doc COPYING README.md doc/manual.html\n%{_bindir}/*\n\n%clean\nrm -rf %{buildroot}\n\n#The changelog is built automatically from Git history\n%changelog\n"
  },
  {
    "path": "misc/packaging/rpmbuild.sh",
    "content": "#!/bin/bash\n\necho Building ninja RPMs..\nGITROOT=$(git rev-parse --show-toplevel)\ncd $GITROOT\n\nVER=1.0\nREL=$(git rev-parse --short HEAD)git\nRPMTOPDIR=$GITROOT/rpm-build\necho \"Ver: $VER, Release: $REL\"\n\n# Create tarball\nmkdir -p $RPMTOPDIR/{SOURCES,SPECS}\ngit archive --format=tar --prefix=ninja-${VER}-${REL}/ HEAD | gzip -c > $RPMTOPDIR/SOURCES/ninja-${VER}-${REL}.tar.gz\n\n# Convert git log to RPM's ChangeLog format (shown with rpm -qp --changelog <rpm file>)\nsed -e \"s/%{ver}/$VER/\" -e \"s/%{rel}/$REL/\" misc/packaging/ninja.spec > $RPMTOPDIR/SPECS/ninja.spec\ngit log --format=\"* %cd %aN%n- (%h) %s%d%n\" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $RPMTOPDIR/SPECS/ninja.spec\n\n# Build SRC and binary RPMs\nrpmbuild    --quiet                       \\\n            --define \"_topdir $RPMTOPDIR\" \\\n            --define \"_rpmdir $PWD\"       \\\n            --define \"_srcrpmdir $PWD\"    \\\n            --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \\\n            -ba $RPMTOPDIR/SPECS/ninja.spec &&\n\nrm -rf $RPMTOPDIR &&\necho Done\n"
  },
  {
    "path": "misc/write_fake_manifests.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"Writes large manifest files, for manifest parser performance testing.\n\nThe generated manifest files are (eerily) similar in appearance and size to the\nones used in the Chromium project.\n\nUsage:\n  python misc/write_fake_manifests.py outdir  # Will run for about 5s.\n\nThe program contains a hardcoded random seed, so it will generate the same\noutput every time it runs.  By changing the seed, it's easy to generate many\ndifferent sets of manifest files.\n\"\"\"\n\nimport argparse\nimport contextlib\nimport os\nimport random\nimport sys\nfrom typing import Generator, Optional, Tuple, List, Set\n\nimport ninja_syntax\n\n\ndef paretoint(avg: float, alpha: float) -> int:\n    \"\"\"Returns a random integer that's avg on average, following a power law.\n    alpha determines the shape of the power curve. alpha has to be larger\n    than 1. The closer alpha is to 1, the higher the variation of the returned\n    numbers.\"\"\"\n    return int(random.paretovariate(alpha) * avg / (alpha / (alpha - 1)))\n\n\n# Based on http://neugierig.org/software/chromium/class-name-generator.html\ndef moar(avg_options: float, p_suffix: float) -> str:\n    kStart = ['render', 'web', 'browser', 'tab', 'content', 'extension', 'url',\n              'file', 'sync', 'content', 'http', 'profile']\n    kOption = ['view', 'host', 'holder', 'container', 'impl', 'ref',\n               'delegate', 'widget', 'proxy', 'stub', 'context',\n               'manager', 'master', 'watcher', 'service', 'file', 'data',\n               'resource', 'device', 'info', 'provider', 'internals', 'tracker',\n               'api', 'layer']\n    kOS = ['win', 'mac', 'aura', 'linux', 'android', 'unittest', 'browsertest']\n    num_options = min(paretoint(avg_options, alpha=4), 5)\n    # The original allows kOption to repeat as long as no consecutive options\n    # repeat.  This version doesn't allow any option repetition.\n    name = [random.choice(kStart)] + random.sample(kOption, num_options)\n    if random.random() < p_suffix:\n        name.append(random.choice(kOS))\n    return '_'.join(name)\n\n\nclass GenRandom(object):\n    def __init__(self, src_dir: str) -> None:\n        self.seen_names: Set[Optional[str]] = set([None])\n        self.seen_defines: Set[Optional[str]] = set([None])\n        self.src_dir = src_dir\n\n    def _unique_string(self, seen: Set[Optional[str]], avg_options: float = 1.3, p_suffix: float = 0.1) -> str:\n        s = None\n        while s in seen:\n            s = moar(avg_options, p_suffix)\n        seen.add(s)\n        return s  # type: ignore # Incompatible return value type\n\n    def _n_unique_strings(self, n: int) -> List[str]:\n        seen: Set[Optional[str]] = set([None])\n        return [self._unique_string(seen, avg_options=3, p_suffix=0.4)\n                for _ in range(n)]\n\n    def target_name(self) -> str:\n        return self._unique_string(p_suffix=0, seen=self.seen_names)\n\n    def path(self) -> str:\n        return os.path.sep.join([\n            self._unique_string(self.seen_names, avg_options=1, p_suffix=0)\n            for _ in range(1 + paretoint(0.6, alpha=4))])\n\n    def src_obj_pairs(self, path: str, name: str) -> List[Tuple[str, str]]:\n        num_sources = paretoint(55, alpha=2) + 1\n        return [(os.path.join(self.src_dir, path, s + '.cc'),\n                 os.path.join('obj', path, '%s.%s.o' % (name, s)))\n                for s in self._n_unique_strings(num_sources)]\n\n    def defines(self) -> List[str]:\n        return [\n            '-DENABLE_' + self._unique_string(self.seen_defines).upper()\n            for _ in range(paretoint(20, alpha=3))]\n\n\nLIB, EXE = 0, 1\nclass Target(object):\n    def __init__(self, gen: GenRandom, kind: int) -> None:\n        self.name = gen.target_name()\n        self.dir_path = gen.path()\n        self.ninja_file_path = os.path.join(\n            'obj', self.dir_path, self.name + '.ninja')\n        self.src_obj_pairs = gen.src_obj_pairs(self.dir_path, self.name)\n        if kind == LIB:\n            self.output = os.path.join('lib' + self.name + '.a')\n        elif kind == EXE:\n            self.output = os.path.join(self.name)\n        self.defines = gen.defines()\n        self.deps: List[Target] = []\n        self.kind = kind\n        self.has_compile_depends = random.random() < 0.4\n\n\ndef write_target_ninja(ninja: ninja_syntax.Writer, target: Target, src_dir: str) -> None:\n    compile_depends = None\n    if target.has_compile_depends:\n      compile_depends = os.path.join(\n          'obj', target.dir_path, target.name + '.stamp')\n      ninja.build(compile_depends, 'stamp', target.src_obj_pairs[0][0])\n      ninja.newline()\n\n    ninja.variable('defines', target.defines)\n    ninja.variable('includes', '-I' + src_dir)\n    ninja.variable('cflags', ['-Wall', '-fno-rtti', '-fno-exceptions'])\n    ninja.newline()\n\n    for src, obj in target.src_obj_pairs:\n        ninja.build(obj, 'cxx', src, implicit=compile_depends)\n    ninja.newline()\n\n    deps = [dep.output for dep in target.deps]\n    libs = [dep.output for dep in target.deps if dep.kind == LIB]\n    if target.kind == EXE:\n        ninja.variable('libs', libs)\n        if sys.platform == \"darwin\":\n            ninja.variable('ldflags', '-Wl,-pie')\n    link = { LIB: 'alink', EXE: 'link'}[target.kind]\n    ninja.build(target.output, link, [obj for _, obj in target.src_obj_pairs],\n                implicit=deps)\n\n\ndef write_sources(target: Target, root_dir: str) -> None:\n    need_main = target.kind == EXE\n\n    includes = []\n\n    # Include siblings.\n    for cc_filename, _ in target.src_obj_pairs:\n        h_filename = os.path.basename(os.path.splitext(cc_filename)[0] + '.h')\n        includes.append(h_filename)\n\n    # Include deps.\n    for dep in target.deps:\n        for cc_filename, _ in dep.src_obj_pairs:\n            h_filename = os.path.basename(\n                os.path.splitext(cc_filename)[0] + '.h')\n            includes.append(\"%s/%s\" % (dep.dir_path, h_filename))\n\n    for cc_filename, _ in target.src_obj_pairs:\n        cc_path = os.path.join(root_dir, cc_filename)\n        h_path = os.path.splitext(cc_path)[0] + '.h'\n        namespace = os.path.basename(target.dir_path)\n        class_ = os.path.splitext(os.path.basename(cc_filename))[0]\n        try:\n            os.makedirs(os.path.dirname(cc_path))\n        except OSError:\n            pass\n\n        with open(h_path, 'w') as f:\n            f.write('namespace %s { struct %s { %s(); }; }' % (namespace,\n                                                               class_, class_))\n        with open(cc_path, 'w') as f:\n            for include in includes:\n                f.write('#include \"%s\"\\n' % include)\n            f.write('\\n')\n            f.write('namespace %s { %s::%s() {} }' % (namespace,\n                                                      class_, class_))\n\n            if need_main:\n                f.write('int main(int argc, char **argv) {}\\n')\n                need_main = False\n\ndef write_master_ninja(master_ninja: ninja_syntax.Writer, targets: List[Target]) -> None:\n    \"\"\"Writes master build.ninja file, referencing all given subninjas.\"\"\"\n    master_ninja.variable('cxx', 'c++')\n    master_ninja.variable('ld', '$cxx')\n    if sys.platform == 'darwin':\n        master_ninja.variable('alink', 'libtool -static')\n    else:\n        master_ninja.variable('alink', 'ar rcs')\n    master_ninja.newline()\n\n    master_ninja.pool('link_pool', depth=4)\n    master_ninja.newline()\n\n    master_ninja.rule('cxx', description='CXX $out',\n      command='$cxx -MMD -MF $out.d $defines $includes $cflags -c $in -o $out',\n      depfile='$out.d', deps='gcc')\n    master_ninja.rule('alink', description='ARCHIVE $out',\n      command='rm -f $out && $alink -o $out $in')\n    master_ninja.rule('link', description='LINK $out', pool='link_pool',\n      command='$ld $ldflags -o $out $in $libs')\n    master_ninja.rule('stamp', description='STAMP $out', command='touch $out')\n    master_ninja.newline()\n\n    for target in targets:\n        master_ninja.subninja(target.ninja_file_path)\n    master_ninja.newline()\n\n    master_ninja.comment('Short names for targets.')\n    for target in targets:\n        if target.name != target.output:\n            master_ninja.build(target.name, 'phony', target.output)\n    master_ninja.newline()\n\n    master_ninja.build('all', 'phony', [target.output for target in targets])\n    master_ninja.default('all')\n\n\n@contextlib.contextmanager\ndef FileWriter(path: str) -> Generator[ninja_syntax.Writer, None, None]:\n    \"\"\"Context manager for a ninja_syntax object writing to a file.\"\"\"\n    try:\n        os.makedirs(os.path.dirname(path))\n    except OSError:\n        pass\n    f = open(path, 'w')\n    yield ninja_syntax.Writer(f)\n    f.close()\n\n\ndef random_targets(num_targets: int, src_dir: str) -> List[Target]:\n    gen = GenRandom(src_dir)\n\n    # N-1 static libraries, and 1 executable depending on all of them.\n    targets = [Target(gen, LIB) for i in range(num_targets - 1)]\n    for i in range(len(targets)):\n        targets[i].deps = [t for t in targets[0:i] if random.random() < 0.05]\n\n    last_target = Target(gen, EXE)\n    last_target.deps = targets[:]\n    last_target.src_obj_pairs = last_target.src_obj_pairs[0:10]  # Trim.\n    targets.append(last_target)\n    return targets\n\n\ndef main() -> None:\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-s', '--sources', nargs=\"?\", const=\"src\",\n        help='write sources to directory (relative to output directory)')\n    parser.add_argument('-t', '--targets', type=int, default=1500,\n                        help='number of targets (default: 1500)')\n    parser.add_argument('-S', '--seed', type=int, help='random seed',\n                        default=12345)\n    parser.add_argument('outdir', help='output directory')\n    args = parser.parse_args()\n    root_dir = args.outdir\n\n    random.seed(args.seed)\n\n    do_write_sources = args.sources is not None\n    src_dir = args.sources if do_write_sources else \"src\"\n\n    targets = random_targets(args.targets, src_dir)\n    for target in targets:\n        with FileWriter(os.path.join(root_dir, target.ninja_file_path)) as n:\n            write_target_ninja(n, target, src_dir)\n\n        if do_write_sources:\n            write_sources(target, root_dir)\n\n    with FileWriter(os.path.join(root_dir, 'build.ninja')) as master_ninja:\n        master_ninja.width = 120\n        write_master_ninja(master_ninja, targets)\n\n\nif __name__ == '__main__':\n    sys.exit(main())  # type: ignore # \"main\" does not return a value\n"
  },
  {
    "path": "misc/zsh-completion",
    "content": "#compdef ninja\n# Copyright 2011 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Add the following to your .zshrc to tab-complete ninja targets\n#   fpath=(path/to/ninja/misc/zsh-completion $fpath)\n\n(( $+functions[_ninja-get-targets] )) || _ninja-get-targets() {\n  dir=\".\"\n  if [ -n \"${opt_args[-C]}\" ];\n  then\n    eval dir=\"${opt_args[-C]}\"\n  fi\n  file=\"build.ninja\"\n  if [ -n \"${opt_args[-f]}\" ];\n  then\n    eval file=\"${opt_args[-f]}\"\n  fi\n  targets_command=\"ninja -f \\\"${file}\\\" -C \\\"${dir}\\\" -t targets all\"\n  eval ${targets_command} 2>/dev/null | cut -d: -f1\n}\n\n(( $+functions[_ninja-get-tools] )) || _ninja-get-tools() {\n  # remove the first line; remove the leading spaces; replace spaces with colon\n  ninja -t list 2> /dev/null | sed -e '1d;s/^ *//;s/ \\+/:/'\n}\n\n(( $+functions[_ninja-get-modes] )) || _ninja-get-modes() {\n  # remove the first line; remove the last line; remove the leading spaces; replace spaces with colon\n  ninja -d list 2> /dev/null | sed -e '1d;$d;s/^ *//;s/ \\+/:/'\n}\n\n(( $+functions[_ninja-modes] )) || _ninja-modes() {\n  local -a modes\n  modes=(${(fo)\"$(_ninja-get-modes)\"})\n  _describe 'modes' modes\n}\n\n(( $+functions[_ninja-tools] )) || _ninja-tools() {\n  local -a tools\n  tools=(${(fo)\"$(_ninja-get-tools)\"})\n  _describe 'tools' tools\n}\n\n(( $+functions[_ninja-targets] )) || _ninja-targets() {\n  local -a targets\n  targets=(${(fo)\"$(_ninja-get-targets)\"})\n  _describe 'targets' targets\n}\n\n_arguments \\\n  '(- *)'{-h,--help}'[Show help]' \\\n  '(- *)--version[Print ninja version]' \\\n  '-C+[Change to directory before doing anything else]:directories:_directories' \\\n  '-f+[Specify input build file (default=build.ninja)]:files:_files' \\\n  '-j+[Run N jobs in parallel (default=number of CPUs available)]:number of jobs' \\\n  '-l+[Do not start new jobs if the load average is greater than N]:number of jobs' \\\n  '-k+[Keep going until N jobs fail (default=1)]:number of jobs' \\\n  '-n[Dry run (do not run commands but act like they succeeded)]' \\\n  '(-v --verbose --quiet)'{-v,--verbose}'[Show all command lines while building]' \\\n  \"(-v --verbose --quiet)--quiet[Don't show progress status, just command output]\" \\\n  '-d+[Enable debugging (use -d list to list modes)]:modes:_ninja-modes' \\\n  '-t+[Run a subtool (use -t list to list subtools)]:tools:_ninja-tools' \\\n  '*::targets:_ninja-targets'\n"
  },
  {
    "path": "src/browse.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"browse.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <vector>\n\n#include \"build/browse_py.h\"\n\nusing namespace std;\n\nvoid RunBrowsePython(State* state, const char* ninja_command,\n                     const char* input_file, int argc, char* argv[]) {\n  // Fork off a Python process and have it run our code via its stdin.\n  // (Actually the Python process becomes the parent.)\n  int pipefd[2];\n  if (pipe(pipefd) < 0) {\n    perror(\"ninja: pipe\");\n    return;\n  }\n\n  pid_t pid = fork();\n  if (pid < 0) {\n    perror(\"ninja: fork\");\n    return;\n  }\n\n  if (pid > 0) {  // Parent.\n    close(pipefd[1]);\n    do {\n      if (dup2(pipefd[0], 0) < 0) {\n        perror(\"ninja: dup2\");\n        break;\n      }\n\n      std::vector<const char *> command;\n      command.push_back(NINJA_PYTHON);\n      command.push_back(\"-\");\n      command.push_back(\"--ninja-command\");\n      command.push_back(ninja_command);\n      command.push_back(\"-f\");\n      command.push_back(input_file);\n      for (int i = 0; i < argc; i++) {\n          command.push_back(argv[i]);\n      }\n      command.push_back(NULL);\n      execvp(command[0], const_cast<char**>(&command[0]));\n      if (errno == ENOENT) {\n        printf(\"ninja: %s is required for the browse tool\\n\", NINJA_PYTHON);\n      } else {\n        perror(\"ninja: execvp\");\n      }\n    } while (false);\n    _exit(1);\n  } else {  // Child.\n    close(pipefd[0]);\n\n    // Write the script file into the stdin of the Python process.\n    // Only write n - 1 bytes, because Python 3.11 does not allow null\n    // bytes in source code anymore, so avoid writing the null string\n    // terminator.\n    // See https://github.com/python/cpython/issues/96670\n    auto kBrowsePyLength = sizeof(kBrowsePy) - 1;\n    ssize_t len = write(pipefd[1], kBrowsePy, kBrowsePyLength);\n    if (len < (ssize_t)kBrowsePyLength)\n      perror(\"ninja: write\");\n    close(pipefd[1]);\n    exit(0);\n  }\n}\n"
  },
  {
    "path": "src/browse.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_BROWSE_H_\n#define NINJA_BROWSE_H_\n\nstruct State;\n\n/// Run in \"browse\" mode, which execs a Python webserver.\n/// \\a ninja_command is the command used to invoke ninja.\n/// \\a args are the number of arguments to be passed to the Python script.\n/// \\a argv are arguments to be passed to the Python script.\n/// This function does not return if it runs successfully.\nvoid RunBrowsePython(State* state, const char* ninja_command,\n                     const char* input_file, int argc, char* argv[]);\n\n#endif  // NINJA_BROWSE_H_\n"
  },
  {
    "path": "src/browse.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright 2001 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Simple web server for browsing dependency graph data.\n\nThis script is inlined into the final executable and spawned by\nit when needed.\n\"\"\"\n\ntry:\n    import http.server as httpserver\n    import socketserver\nexcept ImportError:\n    import BaseHTTPServer as httpserver  # type: ignore # Name \"httpserver\" already defined\n    import SocketServer as socketserver  # type: ignore # Name \"socketserver\" already defined\nimport argparse\nimport os\nimport socket\nimport subprocess\nimport sys\nimport webbrowser\nif sys.version_info >= (3, 2):\n    from html import escape\nelse:\n    from cgi import escape\ntry:\n    from urllib.request import unquote  # type: ignore # Module \"urllib.request\" has no attribute \"unquote\"\nexcept ImportError:\n    from urllib2 import unquote\nfrom collections import namedtuple\nfrom typing import Tuple, Any\n\nNode = namedtuple('Node', ['inputs', 'rule', 'target', 'outputs'])\n\n# Ideally we'd allow you to navigate to a build edge or a build node,\n# with appropriate views for each.  But there's no way to *name* a build\n# edge so we can only display nodes.\n#\n# For a given node, it has at most one input edge, which has n\n# different inputs.  This becomes node.inputs.  (We leave out the\n# outputs of the input edge due to what follows.)  The node can have\n# multiple dependent output edges.  Rather than attempting to display\n# those, they are summarized by taking the union of all their outputs.\n#\n# This means there's no single view that shows you all inputs and outputs\n# of an edge.  But I think it's less confusing than alternatives.\n\ndef match_strip(line: str, prefix: str) -> Tuple[bool, str]:\n    if not line.startswith(prefix):\n        return (False, line)\n    return (True, line[len(prefix):])\n\ndef html_escape(text: str) -> str:\n    return escape(text, quote=True)\n\ndef parse(text: str) -> Node:\n    lines = iter(text.split('\\n'))\n\n    target = None\n    rule = None\n    inputs = []\n    outputs = []\n\n    try:\n        target = next(lines)[:-1]  # strip trailing colon\n\n        line = next(lines)\n        (match, rule) = match_strip(line, '  input: ')\n        if match:\n            (match, line) = match_strip(next(lines), '    ')\n            while match:\n                type = \"\"\n                (match, line) = match_strip(line, '| ')\n                if match:\n                    type = 'implicit'\n                (match, line) = match_strip(line, '|| ')\n                if match:\n                    type = 'order-only'\n                inputs.append((line, type))\n                (match, line) = match_strip(next(lines), '    ')\n\n        match, _ = match_strip(line, '  outputs:')\n        if match:\n            (match, line) = match_strip(next(lines), '    ')\n            while match:\n                outputs.append(line)\n                (match, line) = match_strip(next(lines), '    ')\n    except StopIteration:\n        pass\n\n    return Node(inputs, rule, target, outputs)\n\ndef create_page(body: str) -> str:\n    return '''<!DOCTYPE html>\n<style>\nbody {\n    font-family: sans;\n    font-size: 0.8em;\n    margin: 4ex;\n}\nh1 {\n    font-weight: normal;\n    font-size: 140%;\n    text-align: center;\n    margin: 0;\n}\nh2 {\n    font-weight: normal;\n    font-size: 120%;\n}\ntt {\n    font-family: WebKitHack, monospace;\n    white-space: nowrap;\n}\n.filelist {\n  -webkit-columns: auto 2;\n}\n</style>\n''' + body\n\ndef generate_html(node: Node) -> str:\n    document = ['<h1><tt>%s</tt></h1>' % html_escape(node.target)]\n\n    if node.inputs:\n        document.append('<h2>target is built using rule <tt>%s</tt> of</h2>' %\n                        html_escape(node.rule))\n        if len(node.inputs) > 0:\n            document.append('<div class=filelist>')\n            for input, type in sorted(node.inputs):\n                extra = ''\n                if type:\n                    extra = ' (%s)' % html_escape(type)\n                document.append('<tt><a href=\"?%s\">%s</a>%s</tt><br>' %\n                                (html_escape(input), html_escape(input), extra))\n            document.append('</div>')\n\n    if node.outputs:\n        document.append('<h2>dependent edges build:</h2>')\n        document.append('<div class=filelist>')\n        for output in sorted(node.outputs):\n            document.append('<tt><a href=\"?%s\">%s</a></tt><br>' %\n                            (html_escape(output), html_escape(output)))\n        document.append('</div>')\n\n    return '\\n'.join(document)\n\ndef ninja_dump(target: str) -> Tuple[str, str, int]:\n    cmd = [args.ninja_command, '-f', args.f, '-t', 'query', target]\n    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,\n                            universal_newlines=True)\n    return proc.communicate() + (proc.returncode,)\n\nclass RequestHandler(httpserver.BaseHTTPRequestHandler):\n    def do_GET(self) -> None:\n        assert self.path[0] == '/'\n        target = unquote(self.path[1:])\n\n        if target == '':\n            self.send_response(302)\n            self.send_header('Location', '?' + args.initial_target)\n            self.end_headers()\n            return\n\n        if not target.startswith('?'):\n            self.send_response(404)\n            self.end_headers()\n            return\n        target = target[1:]\n\n        ninja_output, ninja_error, exit_code = ninja_dump(target)\n        if exit_code == 0:\n            page_body = generate_html(parse(ninja_output.strip()))\n        else:\n            # Relay ninja's error message.\n            page_body = '<h1><tt>%s</tt></h1>' % html_escape(ninja_error)\n\n        self.send_response(200)\n        self.end_headers()\n        self.wfile.write(create_page(page_body).encode('utf-8'))\n\n    def log_message(self, format: str, *args: Any) -> None:\n        pass  # Swallow console spam.\n\nparser = argparse.ArgumentParser(prog='ninja -t browse')\nparser.add_argument('--port', '-p', default=8000, type=int,\n    help='Port number to use (default %(default)d)')\nparser.add_argument('--hostname', '-a', default='localhost', type=str,\n    help='Hostname to bind to (default %(default)s)')\nparser.add_argument('--no-browser', action='store_true',\n    help='Do not open a webbrowser on startup.')\n\nparser.add_argument('--ninja-command', default='ninja',\n    help='Path to ninja binary (default %(default)s)')\nparser.add_argument('-f', default='build.ninja',\n    help='Path to build.ninja file (default %(default)s)')\nparser.add_argument('initial_target', default='all', nargs='?',\n    help='Initial target to show (default %(default)s)')\n\nclass HTTPServer(socketserver.ThreadingMixIn, httpserver.HTTPServer):\n    # terminate server immediately when Python exits.\n    daemon_threads = True\n\nargs = parser.parse_args()\nport = args.port\nhostname = args.hostname\nhttpd = HTTPServer((hostname,port), RequestHandler)\ntry:\n    if hostname == \"\":\n        hostname = socket.gethostname()\n    print('Web server running on %s:%d, ctl-C to abort...' % (hostname,port) )\n    print('Web server pid %d' % os.getpid(), file=sys.stderr )\n    if not args.no_browser:\n        webbrowser.open_new('http://%s:%s' % (hostname, port) )\n    httpd.serve_forever()\nexcept KeyboardInterrupt:\n    print()\n    pass  # Swallow console spam.\n\n\n"
  },
  {
    "path": "src/build.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"build.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <functional>\n#include <unordered_set>\n\n#if defined(__SVR4) && defined(__sun)\n#include <sys/termios.h>\n#endif\n\n#include \"build_log.h\"\n#include \"clparser.h\"\n#include \"debug_flags.h\"\n#include \"depfile_parser.h\"\n#include \"deps_log.h\"\n#include \"disk_interface.h\"\n#include \"exit_status.h\"\n#include \"explanations.h\"\n#include \"graph.h\"\n#include \"jobserver.h\"\n#include \"metrics.h\"\n#include \"state.h\"\n#include \"status.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nnamespace {\n\n/// A CommandRunner that doesn't actually run the commands.\nstruct DryRunCommandRunner : public CommandRunner {\n  // Overridden from CommandRunner:\n  size_t CanRunMore() const override;\n  bool StartCommand(Edge* edge) override;\n  bool WaitForCommand(Result* result) override;\n\n private:\n  queue<Edge*> finished_;\n};\n\nsize_t DryRunCommandRunner::CanRunMore() const {\n  return SIZE_MAX;\n}\n\nbool DryRunCommandRunner::StartCommand(Edge* edge) {\n  finished_.push(edge);\n  return true;\n}\n\nbool DryRunCommandRunner::WaitForCommand(Result* result) {\n   if (finished_.empty())\n     return false;\n\n   result->status = ExitSuccess;\n   result->edge = finished_.front();\n   finished_.pop();\n   return true;\n}\n\n}  // namespace\n\nPlan::Plan(Builder* builder)\n  : builder_(builder)\n  , command_edges_(0)\n  , wanted_edges_(0)\n{}\n\nvoid Plan::Reset() {\n  command_edges_ = 0;\n  wanted_edges_ = 0;\n  ready_.clear();\n  want_.clear();\n}\n\nbool Plan::AddTarget(const Node* target, string* err) {\n  targets_.push_back(target);\n  return AddSubTarget(target, NULL, err, NULL);\n}\n\nbool Plan::AddSubTarget(const Node* node, const Node* dependent, string* err,\n                        set<Edge*>* dyndep_walk) {\n  Edge* edge = node->in_edge();\n  if (!edge) {\n     // Leaf node, this can be either a regular input from the manifest\n     // (e.g. a source file), or an implicit input from a depfile or dyndep\n     // file. In the first case, a dirty flag means the file is missing,\n     // and the build should stop. In the second, do not do anything here\n     // since there is no producing edge to add to the plan.\n     if (node->dirty() && !node->generated_by_dep_loader()) {\n       string referenced;\n       if (dependent)\n         referenced = \", needed by '\" + dependent->path() + \"',\";\n       *err = \"'\" + node->path() + \"'\" + referenced +\n              \" missing and no known rule to make it\";\n     }\n     return false;\n  }\n\n  if (edge->outputs_ready())\n    return false;  // Don't need to do anything.\n\n  // If an entry in want_ does not already exist for edge, create an entry which\n  // maps to kWantNothing, indicating that we do not want to build this entry itself.\n  pair<map<Edge*, Want>::iterator, bool> want_ins =\n    want_.insert(make_pair(edge, kWantNothing));\n  Want& want = want_ins.first->second;\n\n  if (dyndep_walk && want == kWantToFinish)\n    return false;  // Don't need to do anything with already-scheduled edge.\n\n  // If we do need to build edge and we haven't already marked it as wanted,\n  // mark it now.\n  if (node->dirty() && want == kWantNothing) {\n    want = kWantToStart;\n    EdgeWanted(edge);\n  }\n\n  if (dyndep_walk)\n    dyndep_walk->insert(edge);\n\n  if (!want_ins.second)\n    return true;  // We've already processed the inputs.\n\n  for (vector<Node*>::iterator i = edge->inputs_.begin();\n       i != edge->inputs_.end(); ++i) {\n    if (!AddSubTarget(*i, node, err, dyndep_walk) && !err->empty())\n      return false;\n  }\n\n  return true;\n}\n\nvoid Plan::EdgeWanted(const Edge* edge) {\n  ++wanted_edges_;\n  if (!edge->is_phony()) {\n    ++command_edges_;\n    if (builder_)\n      builder_->status_->EdgeAddedToPlan(edge);\n  }\n}\n\nEdge* Plan::FindWork() {\n  if (ready_.empty())\n    return NULL;\n\n  Edge* work = ready_.top();\n\n  // If jobserver mode is enabled, try to acquire a token first,\n  // and return null in case of failure.\n  if (builder_ && builder_->jobserver_.get()) {\n    work->job_slot_ = builder_->jobserver_->TryAcquire();\n    if (!work->job_slot_.IsValid())\n      return nullptr;\n  }\n\n  ready_.pop();\n  return work;\n}\n\nvoid Plan::ScheduleWork(map<Edge*, Want>::iterator want_e) {\n  if (want_e->second == kWantToFinish) {\n    // This edge has already been scheduled.  We can get here again if an edge\n    // and one of its dependencies share an order-only input, or if a node\n    // duplicates an out edge (see https://github.com/ninja-build/ninja/pull/519).\n    // Avoid scheduling the work again.\n    return;\n  }\n  assert(want_e->second == kWantToStart);\n  want_e->second = kWantToFinish;\n\n  Edge* edge = want_e->first;\n  Pool* pool = edge->pool();\n  if (pool->ShouldDelayEdge()) {\n    pool->DelayEdge(edge);\n    pool->RetrieveReadyEdges(&ready_);\n  } else {\n    pool->EdgeScheduled(*edge);\n    ready_.push(edge);\n  }\n}\n\nbool Plan::EdgeFinished(Edge* edge, EdgeResult result, string* err) {\n  map<Edge*, Want>::iterator e = want_.find(edge);\n  assert(e != want_.end());\n  bool directly_wanted = e->second != kWantNothing;\n\n  // See if this job frees up any delayed jobs.\n  if (directly_wanted)\n    edge->pool()->EdgeFinished(*edge);\n  edge->pool()->RetrieveReadyEdges(&ready_);\n\n  // Release job slot if needed.\n  if (builder_ && builder_->jobserver_.get())\n    builder_->jobserver_->Release(std::move(edge->job_slot_));\n\n  // The rest of this function only applies to successful commands.\n  if (result != kEdgeSucceeded)\n    return true;\n\n  if (directly_wanted)\n    --wanted_edges_;\n  want_.erase(e);\n  edge->outputs_ready_ = true;\n\n  // Check off any nodes we were waiting for with this edge.\n  for (vector<Node*>::iterator o = edge->outputs_.begin();\n       o != edge->outputs_.end(); ++o) {\n    if (!NodeFinished(*o, err))\n      return false;\n  }\n  return true;\n}\n\nbool Plan::NodeFinished(Node* node, string* err) {\n  // If this node provides dyndep info, load it now.\n  if (node->dyndep_pending()) {\n    assert(builder_ && \"dyndep requires Plan to have a Builder\");\n    // Load the now-clean dyndep file.  This will also update the\n    // build plan and schedule any new work that is ready.\n    return builder_->LoadDyndeps(node, err);\n  }\n\n  // See if we we want any edges from this node.\n  for (vector<Edge*>::const_iterator oe = node->out_edges().begin();\n       oe != node->out_edges().end(); ++oe) {\n    map<Edge*, Want>::iterator want_e = want_.find(*oe);\n    if (want_e == want_.end())\n      continue;\n\n    // See if the edge is now ready.\n    if (!EdgeMaybeReady(want_e, err))\n      return false;\n  }\n  return true;\n}\n\nbool Plan::EdgeMaybeReady(map<Edge*, Want>::iterator want_e, string* err) {\n  Edge* edge = want_e->first;\n  if (edge->AllInputsReady()) {\n    if (want_e->second != kWantNothing) {\n      ScheduleWork(want_e);\n    } else {\n      // We do not need to build this edge, but we might need to build one of\n      // its dependents.\n      if (!EdgeFinished(edge, kEdgeSucceeded, err))\n        return false;\n    }\n  }\n  return true;\n}\n\nbool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) {\n  node->set_dirty(false);\n\n  for (vector<Edge*>::const_iterator oe = node->out_edges().begin();\n       oe != node->out_edges().end(); ++oe) {\n    // Don't process edges that we don't actually want.\n    map<Edge*, Want>::iterator want_e = want_.find(*oe);\n    if (want_e == want_.end() || want_e->second == kWantNothing)\n      continue;\n\n    // Don't attempt to clean an edge if it failed to load deps.\n    if ((*oe)->deps_missing_)\n      continue;\n\n    // If all non-order-only inputs for this edge are now clean,\n    // we might have changed the dirty state of the outputs.\n    vector<Node*>::iterator\n        begin = (*oe)->inputs_.begin(),\n        end = (*oe)->inputs_.end() - (*oe)->order_only_deps_;\n    if (find_if(begin, end, mem_fn(&Node::dirty)) == end) {\n      // Recompute most_recent_input.\n      Node* most_recent_input = NULL;\n      for (vector<Node*>::iterator i = begin; i != end; ++i) {\n        if (!most_recent_input || (*i)->mtime() > most_recent_input->mtime())\n          most_recent_input = *i;\n      }\n\n      // Now, this edge is dirty if any of the outputs are dirty.\n      // If the edge isn't dirty, clean the outputs and mark the edge as not\n      // wanted.\n      bool outputs_dirty = false;\n      if (!scan->RecomputeOutputsDirty(*oe, most_recent_input,\n                                       &outputs_dirty, err)) {\n        return false;\n      }\n      if (!outputs_dirty) {\n        for (vector<Node*>::iterator o = (*oe)->outputs_.begin();\n             o != (*oe)->outputs_.end(); ++o) {\n          if (!CleanNode(scan, *o, err))\n            return false;\n        }\n\n        want_e->second = kWantNothing;\n        --wanted_edges_;\n        if (!(*oe)->is_phony()) {\n          --command_edges_;\n          if (builder_)\n            builder_->status_->EdgeRemovedFromPlan(*oe);\n        }\n      }\n    }\n  }\n  return true;\n}\n\nbool Plan::DyndepsLoaded(DependencyScan* scan, const Node* node,\n                         const DyndepFile& ddf, string* err) {\n  // Recompute the dirty state of all our direct and indirect dependents now\n  // that our dyndep information has been loaded.\n  if (!RefreshDyndepDependents(scan, node, err))\n    return false;\n\n  // We loaded dyndep information for those out_edges of the dyndep node that\n  // specify the node in a dyndep binding, but they may not be in the plan.\n  // Starting with those already in the plan, walk newly-reachable portion\n  // of the graph through the dyndep-discovered dependencies.\n\n  // Find edges in the the build plan for which we have new dyndep info.\n  std::vector<DyndepFile::const_iterator> dyndep_roots;\n  for (DyndepFile::const_iterator oe = ddf.begin(); oe != ddf.end(); ++oe) {\n    Edge* edge = oe->first;\n\n    // If the edge outputs are ready we do not need to consider it here.\n    if (edge->outputs_ready())\n      continue;\n\n    map<Edge*, Want>::iterator want_e = want_.find(edge);\n\n    // If the edge has not been encountered before then nothing already in the\n    // plan depends on it so we do not need to consider the edge yet either.\n    if (want_e == want_.end())\n      continue;\n\n    // This edge is already in the plan so queue it for the walk.\n    dyndep_roots.push_back(oe);\n  }\n\n  // Walk dyndep-discovered portion of the graph to add it to the build plan.\n  std::set<Edge*> dyndep_walk;\n  for (std::vector<DyndepFile::const_iterator>::iterator\n       oei = dyndep_roots.begin(); oei != dyndep_roots.end(); ++oei) {\n    DyndepFile::const_iterator oe = *oei;\n    for (vector<Node*>::const_iterator i = oe->second.implicit_inputs_.begin();\n         i != oe->second.implicit_inputs_.end(); ++i) {\n      if (!AddSubTarget(*i, oe->first->outputs_[0], err, &dyndep_walk) &&\n          !err->empty())\n        return false;\n    }\n  }\n\n  // Add out edges from this node that are in the plan (just as\n  // Plan::NodeFinished would have without taking the dyndep code path).\n  for (vector<Edge*>::const_iterator oe = node->out_edges().begin();\n       oe != node->out_edges().end(); ++oe) {\n    map<Edge*, Want>::iterator want_e = want_.find(*oe);\n    if (want_e == want_.end())\n      continue;\n    dyndep_walk.insert(want_e->first);\n  }\n\n  // See if any encountered edges are now ready.\n  for (set<Edge*>::iterator wi = dyndep_walk.begin();\n       wi != dyndep_walk.end(); ++wi) {\n    map<Edge*, Want>::iterator want_e = want_.find(*wi);\n    if (want_e == want_.end())\n      continue;\n    if (!EdgeMaybeReady(want_e, err))\n      return false;\n  }\n\n  return true;\n}\n\nbool Plan::RefreshDyndepDependents(DependencyScan* scan, const Node* node,\n                                   string* err) {\n  // Collect the transitive closure of dependents and mark their edges\n  // as not yet visited by RecomputeDirty.\n  set<Node*> dependents;\n  UnmarkDependents(node, &dependents);\n\n  // Update the dirty state of all dependents and check if their edges\n  // have become wanted.\n  for (set<Node*>::iterator i = dependents.begin();\n       i != dependents.end(); ++i) {\n    Node* n = *i;\n\n    // Check if this dependent node is now dirty.  Also checks for new cycles.\n    std::vector<Node*> validation_nodes;\n    if (!scan->RecomputeDirty(n, &validation_nodes, err))\n      return false;\n\n    // Add any validation nodes found during RecomputeDirty as new top level\n    // targets.\n    for (std::vector<Node*>::iterator v = validation_nodes.begin();\n         v != validation_nodes.end(); ++v) {\n      if (Edge* in_edge = (*v)->in_edge()) {\n        if (!in_edge->outputs_ready() &&\n            !AddTarget(*v, err)) {\n          return false;\n        }\n      }\n    }\n    if (!n->dirty())\n      continue;\n\n    // This edge was encountered before.  However, we may not have wanted to\n    // build it if the outputs were not known to be dirty.  With dyndep\n    // information an output is now known to be dirty, so we want the edge.\n    Edge* edge = n->in_edge();\n    assert(edge && !edge->outputs_ready());\n    map<Edge*, Want>::iterator want_e = want_.find(edge);\n    assert(want_e != want_.end());\n    if (want_e->second == kWantNothing) {\n      want_e->second = kWantToStart;\n      EdgeWanted(edge);\n    }\n  }\n  return true;\n}\n\nvoid Plan::UnmarkDependents(const Node* node, set<Node*>* dependents) {\n  for (vector<Edge*>::const_iterator oe = node->out_edges().begin();\n       oe != node->out_edges().end(); ++oe) {\n    Edge* edge = *oe;\n\n    map<Edge*, Want>::iterator want_e = want_.find(edge);\n    if (want_e == want_.end())\n      continue;\n\n    if (edge->mark_ != Edge::VisitNone) {\n      edge->mark_ = Edge::VisitNone;\n      for (vector<Node*>::iterator o = edge->outputs_.begin();\n           o != edge->outputs_.end(); ++o) {\n        if (dependents->insert(*o).second)\n          UnmarkDependents(*o, dependents);\n      }\n    }\n  }\n}\n\nnamespace {\n\n// Heuristic for edge priority weighting.\n// Phony edges are free (0 cost), all other edges are weighted equally.\nint64_t EdgeWeightHeuristic(Edge *edge) {\n  return edge->is_phony() ? 0 : 1;\n}\n\n}  // namespace\n\nvoid Plan::ComputeCriticalPath() {\n  METRIC_RECORD(\"ComputeCriticalPath\");\n\n  // Convenience class to perform a topological sort of all edges\n  // reachable from a set of unique targets. Usage is:\n  //\n  // 1) Create instance.\n  //\n  // 2) Call VisitTarget() as many times as necessary.\n  //    Note that duplicate targets are properly ignored.\n  //\n  // 3) Call result() to get a sorted list of edges,\n  //    where each edge appears _after_ its parents,\n  //    i.e. the edges producing its inputs, in the list.\n  //\n  struct TopoSort {\n    void VisitTarget(const Node* target) {\n      Edge* producer = target->in_edge();\n      if (producer)\n        Visit(producer);\n    }\n\n    const std::vector<Edge*>& result() const { return sorted_edges_; }\n\n   private:\n    // Implementation note:\n    //\n    // This is the regular depth-first-search algorithm described\n    // at https://en.wikipedia.org/wiki/Topological_sorting, except\n    // that:\n    //\n    // - Edges are appended to the end of the list, for performance\n    //   reasons. Hence the order used in result().\n    //\n    // - Since the graph cannot have any cycles, temporary marks\n    //   are not necessary, and a simple set is used to record\n    //   which edges have already been visited.\n    //\n    void Visit(Edge* edge) {\n      auto insertion = visited_set_.emplace(edge);\n      if (!insertion.second)\n        return;\n\n      for (const Node* input : edge->inputs_) {\n        Edge* producer = input->in_edge();\n        if (producer)\n          Visit(producer);\n      }\n      sorted_edges_.push_back(edge);\n    }\n\n    std::unordered_set<Edge*> visited_set_;\n    std::vector<Edge*> sorted_edges_;\n  };\n\n  TopoSort topo_sort;\n  for (const Node* target : targets_) {\n    topo_sort.VisitTarget(target);\n  }\n\n  const auto& sorted_edges = topo_sort.result();\n\n  // First, reset all weights to 1.\n  for (Edge* edge : sorted_edges)\n    edge->set_critical_path_weight(EdgeWeightHeuristic(edge));\n\n  // Second propagate / increment weights from\n  // children to parents. Scan the list\n  // in reverse order to do so.\n  for (auto reverse_it = sorted_edges.rbegin();\n       reverse_it != sorted_edges.rend(); ++reverse_it) {\n    Edge* edge = *reverse_it;\n    int64_t edge_weight = edge->critical_path_weight();\n\n    for (const Node* input : edge->inputs_) {\n      Edge* producer = input->in_edge();\n      if (!producer)\n        continue;\n\n      int64_t producer_weight = producer->critical_path_weight();\n      int64_t candidate_weight = edge_weight + EdgeWeightHeuristic(producer);\n      if (candidate_weight > producer_weight)\n        producer->set_critical_path_weight(candidate_weight);\n    }\n  }\n}\n\nvoid Plan::ScheduleInitialEdges() {\n  // Add ready edges to queue.\n  assert(ready_.empty());\n  std::set<Pool*> pools;\n\n  for (std::map<Edge*, Plan::Want>::iterator it = want_.begin(),\n           end = want_.end(); it != end; ++it) {\n    Edge* edge = it->first;\n    Plan::Want want = it->second;\n    if (want == kWantToStart && edge->AllInputsReady()) {\n      Pool* pool = edge->pool();\n      if (pool->ShouldDelayEdge()) {\n        pool->DelayEdge(edge);\n        pools.insert(pool);\n      } else {\n        ScheduleWork(it);\n      }\n    }\n  }\n\n  // Call RetrieveReadyEdges only once at the end so higher priority\n  // edges are retrieved first, not the ones that happen to be first\n  // in the want_ map.\n  for (std::set<Pool*>::iterator it=pools.begin(),\n           end = pools.end(); it != end; ++it) {\n    (*it)->RetrieveReadyEdges(&ready_);\n  }\n}\n\nvoid Plan::PrepareQueue() {\n  ComputeCriticalPath();\n  ScheduleInitialEdges();\n}\n\nvoid Plan::Dump() const {\n  printf(\"pending: %d\\n\", (int)want_.size());\n  for (map<Edge*, Want>::const_iterator e = want_.begin(); e != want_.end(); ++e) {\n    if (e->second != kWantNothing)\n      printf(\"want \");\n    e->first->Dump();\n  }\n  printf(\"ready: %d\\n\", (int)ready_.size());\n}\n\nBuilder::Builder(State* state, const BuildConfig& config, BuildLog* build_log,\n                 DepsLog* deps_log, DiskInterface* disk_interface,\n                 Status* status, int64_t start_time_millis)\n    : state_(state), config_(config), plan_(this), status_(status),\n      start_time_millis_(start_time_millis), disk_interface_(disk_interface),\n      explanations_(g_explaining ? new Explanations() : nullptr),\n      scan_(state, build_log, deps_log, disk_interface,\n            &config_.depfile_parser_options, explanations_.get()) {\n  lock_file_path_ = \".ninja_lock\";\n  string build_dir = state_->bindings_.LookupVariable(\"builddir\");\n  if (!build_dir.empty())\n    lock_file_path_ = build_dir + \"/\" + lock_file_path_;\n  status_->SetExplanations(explanations_.get());\n}\n\nBuilder::~Builder() {\n  Cleanup();\n  status_->SetExplanations(nullptr);\n}\n\nvoid Builder::Cleanup() {\n  if (command_runner_.get()) {\n    vector<Edge*> active_edges = command_runner_->GetActiveEdges();\n    command_runner_->Abort();\n\n    for (vector<Edge*>::iterator e = active_edges.begin();\n         e != active_edges.end(); ++e) {\n      string depfile = (*e)->GetUnescapedDepfile();\n      for (vector<Node*>::iterator o = (*e)->outputs_.begin();\n           o != (*e)->outputs_.end(); ++o) {\n        // Only delete this output if it was actually modified.  This is\n        // important for things like the generator where we don't want to\n        // delete the manifest file if we can avoid it.  But if the rule\n        // uses a depfile, always delete.  (Consider the case where we\n        // need to rebuild an output because of a modified header file\n        // mentioned in a depfile, and the command touches its depfile\n        // but is interrupted before it touches its output file.)\n        string err;\n        TimeStamp new_mtime = disk_interface_->Stat((*o)->path(), &err);\n        if (new_mtime == -1)  // Log and ignore Stat() errors.\n          status_->Error(\"%s\", err.c_str());\n        if (!depfile.empty() || (*o)->mtime() != new_mtime)\n          disk_interface_->RemoveFile((*o)->path());\n      }\n      if (!depfile.empty())\n        disk_interface_->RemoveFile(depfile);\n    }\n  }\n\n  string err;\n  if (disk_interface_->Stat(lock_file_path_, &err) > 0)\n    disk_interface_->RemoveFile(lock_file_path_);\n}\n\nNode* Builder::AddTarget(const string& name, string* err) {\n  Node* node = state_->LookupNode(name);\n  if (!node) {\n    *err = \"unknown target: '\" + name + \"'\";\n    return NULL;\n  }\n  if (!AddTarget(node, err))\n    return NULL;\n  return node;\n}\n\nbool Builder::AddTarget(Node* target, string* err) {\n  std::vector<Node*> validation_nodes;\n  if (!scan_.RecomputeDirty(target, &validation_nodes, err))\n    return false;\n\n  Edge* in_edge = target->in_edge();\n  if (!in_edge || !in_edge->outputs_ready()) {\n    if (!plan_.AddTarget(target, err)) {\n      return false;\n    }\n  }\n\n  // Also add any validation nodes found during RecomputeDirty as top level\n  // targets.\n  for (std::vector<Node*>::iterator n = validation_nodes.begin();\n       n != validation_nodes.end(); ++n) {\n    if (Edge* validation_in_edge = (*n)->in_edge()) {\n      if (!validation_in_edge->outputs_ready() &&\n          !plan_.AddTarget(*n, err)) {\n        return false;\n      }\n    }\n  }\n\n  return true;\n}\n\nbool Builder::AlreadyUpToDate() const {\n  return !plan_.more_to_do();\n}\n\nExitStatus Builder::Build(string* err) {\n  assert(!AlreadyUpToDate());\n  plan_.PrepareQueue();\n\n  int pending_commands = 0;\n  int failures_allowed = config_.failures_allowed;\n\n  // Set up the command runner if we haven't done so already.\n  if (!command_runner_.get()) {\n    if (config_.dry_run)\n      command_runner_.reset(new DryRunCommandRunner);\n    else\n      command_runner_.reset(CommandRunner::factory(config_, jobserver_.get()));\n    ;\n  }\n\n  // We are about to start the build process.\n  status_->BuildStarted();\n\n  // This main loop runs the entire build process.\n  // It is structured like this:\n  // First, we attempt to start as many commands as allowed by the\n  // command runner.\n  // Second, we attempt to wait for / reap the next finished command.\n  while (plan_.more_to_do()) {\n    // See if we can start any more commands.\n    if (failures_allowed) {\n      size_t capacity = command_runner_->CanRunMore();\n      while (capacity > 0) {\n        Edge* edge = plan_.FindWork();\n        if (!edge)\n          break;\n\n        if (edge->GetBindingBool(\"generator\")) {\n          scan_.build_log()->Close();\n        }\n\n        if (!StartEdge(edge, err)) {\n          Cleanup();\n          status_->BuildFinished();\n          return ExitFailure;\n        }\n\n        if (edge->is_phony()) {\n          if (!plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, err)) {\n            Cleanup();\n            status_->BuildFinished();\n            return ExitFailure;\n          }\n        } else {\n          ++pending_commands;\n\n          --capacity;\n\n          // Re-evaluate capacity.\n          size_t current_capacity = command_runner_->CanRunMore();\n          if (current_capacity < capacity)\n            capacity = current_capacity;\n        }\n      }\n\n       // We are finished with all work items and have no pending\n       // commands. Therefore, break out of the main loop.\n       if (pending_commands == 0 && !plan_.more_to_do()) break;\n    }\n\n    // See if we can reap any finished commands.\n    if (pending_commands) {\n      CommandRunner::Result result;\n      if (!command_runner_->WaitForCommand(&result) ||\n          result.status == ExitInterrupted) {\n        Cleanup();\n        status_->BuildFinished();\n        *err = \"interrupted by user\";\n        return result.status;\n      }\n\n      --pending_commands;\n      bool command_finished = FinishCommand(&result, err);\n      SetFailureCode(result.status);\n      if (!command_finished) {\n        Cleanup();\n        status_->BuildFinished();\n        if (result.success()) {\n          // If the command pretend succeeded, the status wasn't set to a proper exit code,\n          // so we set it to ExitFailure.\n          result.status = ExitFailure;\n          SetFailureCode(result.status);\n        }\n        return result.status;\n      }\n\n      if (!result.success()) {\n        if (failures_allowed)\n          failures_allowed--;\n      }\n\n      // We made some progress; start the main loop over.\n      continue;\n    }\n\n    // If we get here, we cannot make any more progress.\n    status_->BuildFinished();\n    if (failures_allowed == 0) {\n      if (config_.failures_allowed > 1)\n        *err = \"subcommands failed\";\n      else\n        *err = \"subcommand failed\";\n    } else if (failures_allowed < config_.failures_allowed)\n      *err = \"cannot make progress due to previous errors\";\n    else\n      *err = \"stuck [this is a bug]\";\n\n    return GetExitCode();\n  }\n\n  status_->BuildFinished();\n  return ExitSuccess;\n}\n\nbool Builder::StartEdge(Edge* edge, string* err) {\n  METRIC_RECORD(\"StartEdge\");\n  if (edge->is_phony())\n    return true;\n\n  int64_t start_time_millis = GetTimeMillis() - start_time_millis_;\n  running_edges_.insert(make_pair(edge, start_time_millis));\n\n  status_->BuildEdgeStarted(edge, start_time_millis);\n\n  TimeStamp build_start = config_.dry_run ? 0 : -1;\n\n  // Create directories necessary for outputs and remember the current\n  // filesystem mtime to record later\n  // XXX: this will block; do we care?\n  for (vector<Node*>::iterator o = edge->outputs_.begin();\n       o != edge->outputs_.end(); ++o) {\n    if (!disk_interface_->MakeDirs((*o)->path()))\n      return false;\n    if (build_start == -1) {\n      disk_interface_->WriteFile(lock_file_path_, \"\", false);\n      build_start = disk_interface_->Stat(lock_file_path_, err);\n      if (build_start == -1)\n        build_start = 0;\n    }\n  }\n\n  edge->command_start_time_ = build_start;\n\n  // Create depfile directory if needed.\n  // XXX: this may also block; do we care?\n  std::string depfile = edge->GetUnescapedDepfile();\n  if (!depfile.empty() && !disk_interface_->MakeDirs(depfile))\n    return false;\n\n  // Create response file, if needed\n  // XXX: this may also block; do we care?\n  string rspfile = edge->GetUnescapedRspfile();\n  if (!rspfile.empty()) {\n    string content = edge->GetBinding(\"rspfile_content\");\n    if (!disk_interface_->WriteFile(rspfile, content, true))\n      return false;\n  }\n\n  // start command computing and run it\n  if (!command_runner_->StartCommand(edge)) {\n    err->assign(\"command '\" + edge->EvaluateCommand() + \"' failed.\");\n    return false;\n  }\n\n  return true;\n}\n\nbool Builder::FinishCommand(CommandRunner::Result* result, string* err) {\n  METRIC_RECORD(\"FinishCommand\");\n\n  Edge* edge = result->edge;\n\n  // First try to extract dependencies from the result, if any.\n  // This must happen first as it filters the command output (we want\n  // to filter /showIncludes output, even on compile failure) and\n  // extraction itself can fail, which makes the command fail from a\n  // build perspective.\n  vector<Node*> deps_nodes;\n  string deps_type = edge->GetBinding(\"deps\");\n  const string deps_prefix = edge->GetBinding(\"msvc_deps_prefix\");\n  if (!deps_type.empty()) {\n    string extract_err;\n    if (!ExtractDeps(result, deps_type, deps_prefix, &deps_nodes,\n                     &extract_err) &&\n        result->success()) {\n      if (!result->output.empty())\n        result->output.append(\"\\n\");\n      result->output.append(extract_err);\n      result->status = ExitFailure;\n    }\n  }\n\n  int64_t start_time_millis, end_time_millis;\n  RunningEdgeMap::iterator it = running_edges_.find(edge);\n  start_time_millis = it->second;\n  end_time_millis = GetTimeMillis() - start_time_millis_;\n  running_edges_.erase(it);\n\n  status_->BuildEdgeFinished(edge, start_time_millis, end_time_millis,\n                             result->status, result->output);\n\n  // The rest of this function only applies to successful commands.\n  if (!result->success()) {\n    return plan_.EdgeFinished(edge, Plan::kEdgeFailed, err);\n  }\n\n  // Restat the edge outputs\n  TimeStamp record_mtime = 0;\n  if (!config_.dry_run) {\n    const bool restat = edge->GetBindingBool(\"restat\");\n    const bool generator = edge->GetBindingBool(\"generator\");\n    bool node_cleaned = false;\n    record_mtime = edge->command_start_time_;\n\n    // restat and generator rules must restat the outputs after the build\n    // has finished. if record_mtime == 0, then there was an error while\n    // attempting to touch/stat the temp file when the edge started and\n    // we should fall back to recording the outputs' current mtime in the\n    // log.\n    if (record_mtime == 0 || restat || generator) {\n      for (vector<Node*>::iterator o = edge->outputs_.begin();\n           o != edge->outputs_.end(); ++o) {\n        TimeStamp new_mtime = disk_interface_->Stat((*o)->path(), err);\n        if (new_mtime == -1)\n          return false;\n        if (new_mtime > record_mtime)\n          record_mtime = new_mtime;\n        if ((*o)->mtime() == new_mtime && restat) {\n          // The rule command did not change the output.  Propagate the clean\n          // state through the build graph.\n          // Note that this also applies to nonexistent outputs (mtime == 0).\n          if (!plan_.CleanNode(&scan_, *o, err))\n            return false;\n          node_cleaned = true;\n        }\n      }\n    }\n    if (node_cleaned) {\n      record_mtime = edge->command_start_time_;\n    }\n  }\n\n  if (!plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, err))\n    return false;\n\n  // Delete any left over response file.\n  string rspfile = edge->GetUnescapedRspfile();\n  if (!rspfile.empty() && !g_keep_rsp)\n    disk_interface_->RemoveFile(rspfile);\n\n  if (scan_.build_log()) {\n    if (!scan_.build_log()->RecordCommand(\n            edge, static_cast<int>(start_time_millis),\n            static_cast<int>(end_time_millis), record_mtime)) {\n      *err = string(\"Error writing to build log: \") + strerror(errno);\n      return false;\n    }\n  }\n\n  if (!deps_type.empty() && !config_.dry_run) {\n    assert(!edge->outputs_.empty() && \"should have been rejected by parser\");\n    for (std::vector<Node*>::const_iterator o = edge->outputs_.begin();\n         o != edge->outputs_.end(); ++o) {\n      TimeStamp deps_mtime = disk_interface_->Stat((*o)->path(), err);\n      if (deps_mtime == -1)\n        return false;\n      if (!scan_.deps_log()->RecordDeps(*o, deps_mtime, deps_nodes)) {\n        *err = std::string(\"Error writing to deps log: \") + strerror(errno);\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nbool Builder::ExtractDeps(CommandRunner::Result* result,\n                          const string& deps_type,\n                          const string& deps_prefix,\n                          vector<Node*>* deps_nodes,\n                          string* err) {\n  if (deps_type == \"msvc\") {\n    CLParser parser;\n    string output;\n    if (!parser.Parse(result->output, deps_prefix, &output, err))\n      return false;\n    result->output = output;\n    for (set<string>::iterator i = parser.includes_.begin();\n         i != parser.includes_.end(); ++i) {\n      // ~0 is assuming that with MSVC-parsed headers, it's ok to always make\n      // all backslashes (as some of the slashes will certainly be backslashes\n      // anyway). This could be fixed if necessary with some additional\n      // complexity in IncludesNormalize::Relativize.\n      deps_nodes->push_back(state_->GetNode(*i, ~0u));\n    }\n  } else if (deps_type == \"gcc\") {\n    string depfile = result->edge->GetUnescapedDepfile();\n    if (depfile.empty()) {\n      *err = string(\"edge with deps=gcc but no depfile makes no sense\");\n      return false;\n    }\n\n    // Read depfile content.  Treat a missing depfile as empty.\n    string content;\n    switch (disk_interface_->ReadFile(depfile, &content, err)) {\n    case DiskInterface::Okay:\n      break;\n    case DiskInterface::NotFound:\n      err->clear();\n      break;\n    case DiskInterface::OtherError:\n      return false;\n    }\n    if (content.empty())\n      return true;\n\n    DepfileParser deps(config_.depfile_parser_options);\n    if (!deps.Parse(&content, err))\n      return false;\n\n    // XXX check depfile matches expected output.\n    deps_nodes->reserve(deps.ins_.size());\n    for (vector<StringPiece>::iterator i = deps.ins_.begin();\n         i != deps.ins_.end(); ++i) {\n      uint64_t slash_bits;\n      CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits);\n      deps_nodes->push_back(state_->GetNode(*i, slash_bits));\n    }\n\n    if (!g_keep_depfile) {\n      if (disk_interface_->RemoveFile(depfile) < 0) {\n        *err = string(\"deleting depfile: \") + strerror(errno) + string(\"\\n\");\n        return false;\n      }\n    }\n  } else {\n    Fatal(\"unknown deps type '%s'\", deps_type.c_str());\n  }\n\n  return true;\n}\n\nbool Builder::LoadDyndeps(Node* node, string* err) {\n  // Load the dyndep information provided by this node.\n  DyndepFile ddf;\n  if (!scan_.LoadDyndeps(node, &ddf, err))\n    return false;\n\n  // Update the build plan to account for dyndep modifications to the graph.\n  if (!plan_.DyndepsLoaded(&scan_, node, ddf, err))\n    return false;\n\n  return true;\n}\n\nvoid Builder::SetFailureCode(ExitStatus code) {\n  // ExitSuccess should not overwrite any error\n  if (code != ExitSuccess) {\n    exit_code_ = code;\n  }\n}\n"
  },
  {
    "path": "src/build.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_BUILD_H_\n#define NINJA_BUILD_H_\n\n#include <cstdio>\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"depfile_parser.h\"\n#include \"exit_status.h\"\n#include \"graph.h\"\n#include \"jobserver.h\"\n#include \"util.h\"  // int64_t\n\nstruct BuildLog;\nstruct Builder;\nstruct DiskInterface;\nstruct Edge;\nstruct Explanations;\nstruct Node;\nstruct State;\nstruct Status;\n\n/// Plan stores the state of a build plan: what we intend to build,\n/// which steps we're ready to execute.\nstruct Plan {\n  Plan(Builder* builder = NULL);\n\n  /// Add a target to our plan (including all its dependencies).\n  /// Returns false if we don't need to build this target; may\n  /// fill in |err| with an error message if there's a problem.\n  bool AddTarget(const Node* target, std::string* err);\n\n  // Pop a ready edge off the queue of edges to build.\n  // Returns NULL if there's no work to do.\n  Edge* FindWork();\n\n  /// Returns true if there's more work to be done.\n  bool more_to_do() const { return wanted_edges_ > 0 && command_edges_ > 0; }\n\n  /// Dumps the current state of the plan.\n  void Dump() const;\n\n  enum EdgeResult {\n    kEdgeFailed,\n    kEdgeSucceeded\n  };\n\n  /// Mark an edge as done building (whether it succeeded or failed).\n  /// If any of the edge's outputs are dyndep bindings of their dependents,\n  /// this loads dynamic dependencies from the nodes' paths.\n  /// Returns 'false' if loading dyndep info fails and 'true' otherwise.\n  bool EdgeFinished(Edge* edge, EdgeResult result, std::string* err);\n\n  /// Clean the given node during the build.\n  /// Return false on error.\n  bool CleanNode(DependencyScan* scan, Node* node, std::string* err);\n\n  /// Number of edges with commands to run.\n  int command_edge_count() const { return command_edges_; }\n\n  /// Reset state.  Clears want and ready sets.\n  void Reset();\n\n  // After all targets have been added, prepares the ready queue for find work.\n  void PrepareQueue();\n\n  /// Update the build plan to account for modifications made to the graph\n  /// by information loaded from a dyndep file.\n  bool DyndepsLoaded(DependencyScan* scan, const Node* node,\n                     const DyndepFile& ddf, std::string* err);\n\n  /// Enumerate possible steps we want for an edge.\n  enum Want\n  {\n    /// We do not want to build the edge, but we might want to build one of\n    /// its dependents.\n    kWantNothing,\n    /// We want to build the edge, but have not yet scheduled it.\n    kWantToStart,\n    /// We want to build the edge, have scheduled it, and are waiting\n    /// for it to complete.\n    kWantToFinish\n  };\n\nprivate:\n  void ComputeCriticalPath();\n  bool RefreshDyndepDependents(DependencyScan* scan, const Node* node, std::string* err);\n  void UnmarkDependents(const Node* node, std::set<Node*>* dependents);\n  bool AddSubTarget(const Node* node, const Node* dependent, std::string* err,\n                    std::set<Edge*>* dyndep_walk);\n\n  // Add edges that kWantToStart into the ready queue\n  // Must be called after ComputeCriticalPath and before FindWork\n  void ScheduleInitialEdges();\n\n  /// Update plan with knowledge that the given node is up to date.\n  /// If the node is a dyndep binding on any of its dependents, this\n  /// loads dynamic dependencies from the node's path.\n  /// Returns 'false' if loading dyndep info fails and 'true' otherwise.\n  bool NodeFinished(Node* node, std::string* err);\n\n  void EdgeWanted(const Edge* edge);\n  bool EdgeMaybeReady(std::map<Edge*, Want>::iterator want_e, std::string* err);\n\n  /// Submits a ready edge as a candidate for execution.\n  /// The edge may be delayed from running, for example if it's a member of a\n  /// currently-full pool.\n  void ScheduleWork(std::map<Edge*, Want>::iterator want_e);\n\n  /// Keep track of which edges we want to build in this plan.  If this map does\n  /// not contain an entry for an edge, we do not want to build the entry or its\n  /// dependents.  If it does contain an entry, the enumeration indicates what\n  /// we want for the edge.\n  std::map<Edge*, Want> want_;\n\n  EdgePriorityQueue ready_;\n\n  Builder* builder_;\n  /// user provided targets in build order, earlier one have higher priority\n  std::vector<const Node*> targets_;\n\n  /// Total number of edges that have commands (not phony).\n  int command_edges_;\n\n  /// Total remaining number of wanted edges.\n  int wanted_edges_;\n};\n\nstruct BuildConfig;\n\n/// CommandRunner is an interface that wraps running the build\n/// subcommands.  This allows tests to abstract out running commands.\n/// RealCommandRunner is an implementation that actually runs commands.\nstruct CommandRunner {\n  virtual ~CommandRunner() {}\n  virtual size_t CanRunMore() const = 0;\n  virtual bool StartCommand(Edge* edge) = 0;\n\n  /// The result of waiting for a command.\n  struct Result {\n    Edge* edge = nullptr;\n    ExitStatus status = ExitFailure;\n    std::string output;\n    bool success() const { return status == ExitSuccess; }\n  };\n  /// Wait for a command to complete, or return false if interrupted.\n  virtual bool WaitForCommand(Result* result) = 0;\n\n  virtual std::vector<Edge*> GetActiveEdges() { return std::vector<Edge*>(); }\n  virtual void Abort() {}\n\n  /// Creates the RealCommandRunner. \\arg jobserver can be nullptr if there\n  /// is no jobserver pool to use.\n  static CommandRunner* factory(const BuildConfig& config,\n                                Jobserver::Client* jobserver);\n};\n\n/// Options (e.g. verbosity, parallelism) passed to a build.\nstruct BuildConfig {\n  BuildConfig() = default;\n\n  enum Verbosity {\n    QUIET,  // No output -- used when testing.\n    NO_STATUS_UPDATE,  // just regular output but suppress status update\n    NORMAL,  // regular output and status update\n    VERBOSE\n  };\n  Verbosity verbosity = NORMAL;\n  bool dry_run = false;\n  int parallelism = 1;\n  bool disable_jobserver_client = false;\n  int failures_allowed = 1;\n  /// The maximum load average we must not exceed. A negative value\n  /// means that we do not have any limit.\n  double max_load_average = -0.0f;\n  DepfileParserOptions depfile_parser_options;\n};\n\n/// Builder wraps the build process: starting commands, updating status.\nstruct Builder {\n  Builder(State* state, const BuildConfig& config, BuildLog* build_log,\n          DepsLog* deps_log, DiskInterface* disk_interface, Status* status,\n          int64_t start_time_millis);\n  ~Builder();\n\n  /// Set Jobserver client instance for this builder.\n  void SetJobserverClient(std::unique_ptr<Jobserver::Client> jobserver_client) {\n    jobserver_ = std::move(jobserver_client);\n  }\n\n  /// Clean up after interrupted commands by deleting output files.\n  void Cleanup();\n\n  Node* AddTarget(const std::string& name, std::string* err);\n\n  /// Add a target to the build, scanning dependencies.\n  /// @return false on error.\n  bool AddTarget(Node* target, std::string* err);\n\n  /// Returns true if the build targets are already up to date.\n  bool AlreadyUpToDate() const;\n\n  /// Run the build.  Returns ExitStatus or the exit code of the last failed job.\n  /// It is an error to call this function when AlreadyUpToDate() is true.\n  ExitStatus Build(std::string* err);\n\n  bool StartEdge(Edge* edge, std::string* err);\n\n  /// Update status ninja logs following a command termination.\n  /// @return false if the build can not proceed further due to a fatal error.\n  bool FinishCommand(CommandRunner::Result* result, std::string* err);\n\n  /// Used for tests.\n  void SetBuildLog(BuildLog* log) {\n    scan_.set_build_log(log);\n  }\n\n  /// Load the dyndep information provided by the given node.\n  bool LoadDyndeps(Node* node, std::string* err);\n\n  State* state_;\n  const BuildConfig& config_;\n  Plan plan_;\n  std::unique_ptr<Jobserver::Client> jobserver_;\n  std::unique_ptr<CommandRunner> command_runner_;\n  Status* status_;\n\n  /// Returns ExitStatus or the exit code of the last failed job\n  /// (doesn't need to be an enum value of ExitStatus)\n  ExitStatus GetExitCode() const { return exit_code_; }\n\n private:\n  bool ExtractDeps(CommandRunner::Result* result, const std::string& deps_type,\n                   const std::string& deps_prefix,\n                   std::vector<Node*>* deps_nodes, std::string* err);\n\n  /// Map of running edge to time the edge started running.\n  typedef std::map<const Edge*, int> RunningEdgeMap;\n  RunningEdgeMap running_edges_;\n\n  /// Time the build started.\n  int64_t start_time_millis_;\n\n  std::string lock_file_path_;\n  DiskInterface* disk_interface_;\n\n  // Only create an Explanations class if '-d explain' is used.\n  std::unique_ptr<Explanations> explanations_;\n\n  DependencyScan scan_;\n\n  /// Keep the global exit code for the build\n  ExitStatus exit_code_ = ExitSuccess;\n  void SetFailureCode(ExitStatus code);\n\n  // Unimplemented copy ctor and operator= ensure we don't copy the auto_ptr.\n  Builder(const Builder &other);        // DO NOT IMPLEMENT\n  void operator=(const Builder &other); // DO NOT IMPLEMENT\n};\n\n#endif  // NINJA_BUILD_H_\n"
  },
  {
    "path": "src/build_log.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// On AIX, inttypes.h gets indirectly included by build_log.h.\n// It's easiest just to ask for the printf format macros right away.\n#ifndef _WIN32\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n#endif\n\n#include \"build_log.h\"\n#include \"disk_interface.h\"\n\n#include <cassert>\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef _WIN32\n#include <inttypes.h>\n#include <unistd.h>\n#endif\n\n#include \"build.h\"\n#include \"graph.h\"\n#include \"metrics.h\"\n#include \"util.h\"\n#if defined(_MSC_VER) && (_MSC_VER < 1800)\n#define strtoll _strtoi64\n#endif\n\n// Implementation details:\n// Each run's log appends to the log file.\n// To load, we run through all log entries in series, throwing away\n// older runs.\n// Once the number of redundant entries exceeds a threshold, we write\n// out a new file and replace the existing one with it.\n\nnamespace {\n\nconst char kFileSignature[] = \"# ninja log v%d\\n\";\nconst int kOldestSupportedVersion = 7;\nconst int kCurrentVersion = 7;\n\n}  // namespace\n\n// static\nuint64_t BuildLog::LogEntry::HashCommand(StringPiece command) {\n  return rapidhash(command.str_, command.len_);\n}\n\nBuildLog::LogEntry::LogEntry(std::string output) : output(std::move(output)) {}\n\nBuildLog::LogEntry::LogEntry(const std::string& output, uint64_t command_hash,\n                             int start_time, int end_time, TimeStamp mtime)\n    : output(output), command_hash(command_hash), start_time(start_time),\n      end_time(end_time), mtime(mtime) {}\n\nBuildLog::BuildLog() = default;\n\nBuildLog::~BuildLog() {\n  Close();\n}\n\nbool BuildLog::OpenForWrite(const std::string& path, const BuildLogUser& user,\n                            std::string* err) {\n  if (needs_recompaction_) {\n    if (!Recompact(path, user, err))\n      return false;\n  }\n\n  assert(!log_file_);\n  log_file_path_ = path;  // we don't actually open the file right now, but will\n                          // do so on the first write attempt\n  return true;\n}\n\nbool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time,\n                             TimeStamp mtime) {\n  std::string command = edge->EvaluateCommand(true);\n  uint64_t command_hash = LogEntry::HashCommand(command);\n  for (std::vector<Node*>::iterator out = edge->outputs_.begin();\n       out != edge->outputs_.end(); ++out) {\n    const std::string& path = (*out)->path();\n    Entries::iterator i = entries_.find(path);\n    LogEntry* log_entry;\n    if (i != entries_.end()) {\n      log_entry = i->second.get();\n    } else {\n      log_entry = new LogEntry(path);\n      // Passes ownership of |log_entry| to the map, but keeps the pointer valid.\n      entries_.emplace(log_entry->output, std::unique_ptr<LogEntry>(log_entry));\n    }\n    log_entry->command_hash = command_hash;\n    log_entry->start_time = start_time;\n    log_entry->end_time = end_time;\n    log_entry->mtime = mtime;\n\n    if (!OpenForWriteIfNeeded()) {\n      return false;\n    }\n    if (log_file_) {\n      if (!WriteEntry(log_file_, *log_entry))\n        return false;\n      if (fflush(log_file_) != 0) {\n          return false;\n      }\n    }\n  }\n  return true;\n}\n\nvoid BuildLog::Close() {\n  OpenForWriteIfNeeded();  // create the file even if nothing has been recorded\n  if (log_file_)\n    fclose(log_file_);\n  log_file_ = NULL;\n}\n\nbool BuildLog::OpenForWriteIfNeeded() {\n  if (log_file_ || log_file_path_.empty()) {\n    return true;\n  }\n  log_file_ = fopen(log_file_path_.c_str(), \"ab\");\n  if (!log_file_) {\n    return false;\n  }\n  if (setvbuf(log_file_, NULL, _IOLBF, BUFSIZ) != 0) {\n    return false;\n  }\n  SetCloseOnExec(fileno(log_file_));\n\n  // Opening a file in append mode doesn't set the file pointer to the file's\n  // end on Windows. Do that explicitly.\n  fseek(log_file_, 0, SEEK_END);\n\n  if (ftell(log_file_) == 0) {\n    if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) {\n      return false;\n    }\n  }\n  return true;\n}\n\nstruct LineReader {\n  explicit LineReader(FILE* file)\n    : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) {\n      memset(buf_, 0, sizeof(buf_));\n  }\n\n  // Reads a \\n-terminated line from the file passed to the constructor.\n  // On return, *line_start points to the beginning of the next line, and\n  // *line_end points to the \\n at the end of the line. If no newline is seen\n  // in a fixed buffer size, *line_end is set to NULL. Returns false on EOF.\n  bool ReadLine(char** line_start, char** line_end) {\n    if (line_start_ >= buf_end_ || !line_end_) {\n      // Buffer empty, refill.\n      size_t size_read = fread(buf_, 1, sizeof(buf_), file_);\n      if (!size_read)\n        return false;\n      line_start_ = buf_;\n      buf_end_ = buf_ + size_read;\n    } else {\n      // Advance to next line in buffer.\n      line_start_ = line_end_ + 1;\n    }\n\n    line_end_ = static_cast<char*>(memchr(line_start_, '\\n', buf_end_ - line_start_));\n    if (!line_end_) {\n      // No newline. Move rest of data to start of buffer, fill rest.\n      size_t already_consumed = line_start_ - buf_;\n      size_t size_rest = (buf_end_ - buf_) - already_consumed;\n      memmove(buf_, line_start_, size_rest);\n\n      size_t read = fread(buf_ + size_rest, 1, sizeof(buf_) - size_rest, file_);\n      buf_end_ = buf_ + size_rest + read;\n      line_start_ = buf_;\n      line_end_ = static_cast<char*>(memchr(line_start_, '\\n', buf_end_ - line_start_));\n    }\n\n    *line_start = line_start_;\n    *line_end = line_end_;\n    return true;\n  }\n\n private:\n  FILE* file_;\n  char buf_[256 << 10];\n  char* buf_end_;  // Points one past the last valid byte in |buf_|.\n\n  char* line_start_;\n  // Points at the next \\n in buf_ after line_start, or NULL.\n  char* line_end_;\n};\n\nLoadStatus BuildLog::Load(const std::string& path, std::string* err) {\n  METRIC_RECORD(\".ninja_log load\");\n  FILE* file = fopen(path.c_str(), \"r\");\n  if (!file) {\n    if (errno == ENOENT)\n      return LOAD_NOT_FOUND;\n    *err = strerror(errno);\n    return LOAD_ERROR;\n  }\n\n  int log_version = 0;\n  int unique_entry_count = 0;\n  int total_entry_count = 0;\n\n  LineReader reader(file);\n  char* line_start = 0;\n  char* line_end = 0;\n  while (reader.ReadLine(&line_start, &line_end)) {\n    if (!log_version) {\n      sscanf(line_start, kFileSignature, &log_version);\n\n      bool invalid_log_version = false;\n      if (log_version < kOldestSupportedVersion) {\n        invalid_log_version = true;\n        *err = \"build log version is too old; starting over\";\n\n      } else if (log_version > kCurrentVersion) {\n        invalid_log_version = true;\n        *err = \"build log version is too new; starting over\";\n      }\n      if (invalid_log_version) {\n        fclose(file);\n        platformAwareUnlink(path.c_str());\n        // Don't report this as a failure. A missing build log will cause\n        // us to rebuild the outputs anyway.\n        return LOAD_NOT_FOUND;\n      }\n    }\n\n    // If no newline was found in this chunk, read the next.\n    if (!line_end)\n      continue;\n\n    const char kFieldSeparator = '\\t';\n\n    char* start = line_start;\n    char* end = static_cast<char*>(memchr(start, kFieldSeparator, line_end - start));\n    if (!end)\n      continue;\n    *end = 0;\n\n    int start_time = 0, end_time = 0;\n    TimeStamp mtime = 0;\n\n    start_time = atoi(start);\n    start = end + 1;\n\n    end = static_cast<char*>(memchr(start, kFieldSeparator, line_end - start));\n    if (!end)\n      continue;\n    *end = 0;\n    end_time = atoi(start);\n    start = end + 1;\n\n    end = static_cast<char*>(memchr(start, kFieldSeparator, line_end - start));\n    if (!end)\n      continue;\n    *end = 0;\n    mtime = strtoll(start, NULL, 10);\n    start = end + 1;\n\n    end = static_cast<char*>(memchr(start, kFieldSeparator, line_end - start));\n    if (!end)\n      continue;\n    std::string output(start, end - start);\n\n    start = end + 1;\n    end = line_end;\n\n    LogEntry* entry;\n    Entries::iterator i = entries_.find(output);\n    if (i != entries_.end()) {\n      entry = i->second.get();\n    } else {\n      entry = new LogEntry(std::move(output));\n      // Passes ownership of |entry| to the map, but keeps the pointer valid.\n      entries_.emplace(entry->output, std::unique_ptr<LogEntry>(entry));\n      ++unique_entry_count;\n    }\n    ++total_entry_count;\n\n    entry->start_time = start_time;\n    entry->end_time = end_time;\n    entry->mtime = mtime;\n    char c = *end; *end = '\\0';\n    entry->command_hash = (uint64_t)strtoull(start, NULL, 16);\n    *end = c;\n  }\n  fclose(file);\n\n  if (!line_start) {\n    return LOAD_SUCCESS; // file was empty\n  }\n\n  // Decide whether it's time to rebuild the log:\n  // - if we're upgrading versions\n  // - if it's getting large\n  int kMinCompactionEntryCount = 100;\n  int kCompactionRatio = 3;\n  if (log_version < kCurrentVersion) {\n    needs_recompaction_ = true;\n  } else if (total_entry_count > kMinCompactionEntryCount &&\n             total_entry_count > unique_entry_count * kCompactionRatio) {\n    needs_recompaction_ = true;\n  }\n\n  return LOAD_SUCCESS;\n}\n\nBuildLog::LogEntry* BuildLog::LookupByOutput(const std::string& path) {\n  Entries::iterator i = entries_.find(path);\n  if (i != entries_.end())\n    return i->second.get();\n  return NULL;\n}\n\nbool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) {\n  return fprintf(f, \"%d\\t%d\\t%\" PRId64 \"\\t%s\\t%\" PRIx64 \"\\n\",\n          entry.start_time, entry.end_time, entry.mtime,\n          entry.output.c_str(), entry.command_hash) > 0;\n}\n\nbool BuildLog::Recompact(const std::string& path, const BuildLogUser& user,\n                         std::string* err) {\n  METRIC_RECORD(\".ninja_log recompact\");\n\n  Close();\n  std::string temp_path = path + \".recompact\";\n  FILE* f = fopen(temp_path.c_str(), \"wb\");\n  if (!f) {\n    *err = strerror(errno);\n    return false;\n  }\n\n  if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {\n    *err = strerror(errno);\n    fclose(f);\n    return false;\n  }\n\n  std::vector<StringPiece> dead_outputs;\n  for (const auto& pair : entries_) {\n    if (user.IsPathDead(pair.first)) {\n      dead_outputs.push_back(pair.first);\n      continue;\n    }\n\n    if (!WriteEntry(f, *pair.second)) {\n      *err = strerror(errno);\n      fclose(f);\n      return false;\n    }\n  }\n\n  for (StringPiece output : dead_outputs)\n    entries_.erase(output);\n\n  fclose(f);\n\n  return ReplaceContent(path, temp_path, err);\n}\n\nbool BuildLog::Restat(const StringPiece path,\n                      const DiskInterface& disk_interface,\n                      const int output_count, char** outputs,\n                      std::string* const err) {\n  METRIC_RECORD(\".ninja_log restat\");\n\n  Close();\n  std::string temp_path = path.AsString() + \".restat\";\n  FILE* f = fopen(temp_path.c_str(), \"wb\");\n  if (!f) {\n    *err = strerror(errno);\n    return false;\n  }\n\n  if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {\n    *err = strerror(errno);\n    fclose(f);\n    return false;\n  }\n  for (auto& pair : entries_) {\n    bool skip = output_count > 0;\n    for (int j = 0; j < output_count; ++j) {\n      if (pair.second->output == outputs[j]) {\n        skip = false;\n        break;\n      }\n    }\n    if (!skip) {\n      const TimeStamp mtime = disk_interface.Stat(pair.second->output, err);\n      if (mtime == -1) {\n        fclose(f);\n        return false;\n      }\n      pair.second->mtime = mtime;\n    }\n\n    if (!WriteEntry(f, *pair.second)) {\n      *err = strerror(errno);\n      fclose(f);\n      return false;\n    }\n  }\n\n  fclose(f);\n\n  return ReplaceContent(path.AsString(), temp_path, err);\n}\n"
  },
  {
    "path": "src/build_log.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_BUILD_LOG_H_\n#define NINJA_BUILD_LOG_H_\n\n#include <stdio.h>\n\n#include <memory>\n#include <string>\n\n#include \"hash_map.h\"\n#include \"load_status.h\"\n#include \"timestamp.h\"\n#include \"util.h\"  // uint64_t\n\nstruct DiskInterface;\nstruct Edge;\n\n/// Can answer questions about the manifest for the BuildLog.\nstruct BuildLogUser {\n  /// Return if a given output is no longer part of the build manifest.\n  /// This is only called during recompaction and doesn't have to be fast.\n  virtual bool IsPathDead(StringPiece s) const = 0;\n};\n\n/// Store a log of every command ran for every build.\n/// It has a few uses:\n///\n/// 1) (hashes of) command lines for existing output files, so we know\n///    when we need to rebuild due to the command changing\n/// 2) timing information, perhaps for generating reports\n/// 3) restat information\nstruct BuildLog {\n  BuildLog();\n  ~BuildLog();\n\n  /// Prepares writing to the log file without actually opening it - that will\n  /// happen when/if it's needed\n  bool OpenForWrite(const std::string& path, const BuildLogUser& user,\n                    std::string* err);\n  bool RecordCommand(Edge* edge, int start_time, int end_time,\n                     TimeStamp mtime = 0);\n  void Close();\n\n  /// Load the on-disk log.\n  LoadStatus Load(const std::string& path, std::string* err);\n\n  struct LogEntry {\n    std::string output;\n    uint64_t command_hash = 0;\n    int start_time = 0;\n    int end_time = 0;\n    TimeStamp mtime = 0;\n\n    static uint64_t HashCommand(StringPiece command);\n\n    // Used by tests.\n    bool operator==(const LogEntry& o) const {\n      return output == o.output && command_hash == o.command_hash &&\n          start_time == o.start_time && end_time == o.end_time &&\n          mtime == o.mtime;\n    }\n\n    explicit LogEntry(std::string output);\n    LogEntry(const std::string& output, uint64_t command_hash,\n             int start_time, int end_time, TimeStamp mtime);\n  };\n\n  /// Lookup a previously-run command by its output path.\n  LogEntry* LookupByOutput(const std::string& path);\n\n  /// Serialize an entry into a log file.\n  bool WriteEntry(FILE* f, const LogEntry& entry);\n\n  /// Rewrite the known log entries, throwing away old data.\n  bool Recompact(const std::string& path, const BuildLogUser& user,\n                 std::string* err);\n\n  /// Restat all outputs in the log\n  bool Restat(StringPiece path, const DiskInterface& disk_interface,\n              int output_count, char** outputs, std::string* err);\n\n  typedef ExternalStringHashMap<std::unique_ptr<LogEntry>>::Type Entries;\n  const Entries& entries() const { return entries_; }\n\n private:\n  /// Should be called before using log_file_. When false is returned, errno\n  /// will be set.\n  bool OpenForWriteIfNeeded();\n\n  Entries entries_;\n  FILE* log_file_ = nullptr;\n  std::string log_file_path_;\n  bool needs_recompaction_ = false;\n};\n\n#endif // NINJA_BUILD_LOG_H_\n"
  },
  {
    "path": "src/build_log_perftest.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"build_log.h\"\n#include \"graph.h\"\n#include \"manifest_parser.h\"\n#include \"state.h\"\n#include \"util.h\"\n#include \"metrics.h\"\n\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\nusing namespace std;\n\nconst char kTestFilename[] = \"BuildLogPerfTest-tempfile\";\n\nstruct NoDeadPaths : public BuildLogUser {\n  virtual bool IsPathDead(StringPiece) const { return false; }\n};\n\nbool WriteTestData(string* err) {\n  BuildLog log;\n\n  NoDeadPaths no_dead_paths;\n  if (!log.OpenForWrite(kTestFilename, no_dead_paths, err))\n    return false;\n\n  /*\n  A histogram of command lengths in chromium. For example, 407 builds,\n  1.4% of all builds, had commands longer than 32 bytes but shorter than 64.\n       32    407   1.4%\n       64    183   0.6%\n      128   1461   5.1%\n      256    791   2.8%\n      512   1314   4.6%\n     1024   6114  21.3%\n     2048  11759  41.0%\n     4096   2056   7.2%\n     8192   4567  15.9%\n    16384     13   0.0%\n    32768      4   0.0%\n    65536      5   0.0%\n  The average command length is 4.1 kB and there were 28674 commands in total,\n  which makes for a total log size of ~120 MB (also counting output filenames).\n\n  Based on this, write 30000 many 4 kB long command lines.\n  */\n\n  // ManifestParser is the only object allowed to create Rules.\n  const size_t kRuleSize = 4000;\n  string long_rule_command = \"gcc \";\n  for (int i = 0; long_rule_command.size() < kRuleSize; ++i) {\n    char buf[80];\n    sprintf(buf, \"-I../../and/arbitrary/but/fairly/long/path/suffixed/%d \", i);\n    long_rule_command += buf;\n  }\n  long_rule_command += \"$in -o $out\\n\";\n\n  State state;\n  ManifestParser parser(&state, NULL);\n  if (!parser.ParseTest(\"rule cxx\\n  command = \" + long_rule_command, err))\n    return false;\n\n  // Create build edges. Using ManifestParser is as fast as using the State api\n  // for edge creation, so just use that.\n  const int kNumCommands = 30000;\n  string build_rules;\n  for (int i = 0; i < kNumCommands; ++i) {\n    char buf[80];\n    sprintf(buf, \"build input%d.o: cxx input%d.cc\\n\", i, i);\n    build_rules += buf;\n  }\n\n  if (!parser.ParseTest(build_rules, err))\n    return false;\n\n  for (int i = 0; i < kNumCommands; ++i) {\n    log.RecordCommand(state.edges_[i],\n                      /*start_time=*/100 * i,\n                      /*end_time=*/100 * i + 1,\n                      /*mtime=*/0);\n  }\n\n  return true;\n}\n\nint main() {\n  vector<int> times;\n  string err;\n\n  if (!WriteTestData(&err)) {\n    fprintf(stderr, \"Failed to write test data: %s\\n\", err.c_str());\n    return 1;\n  }\n\n  {\n    // Read once to warm up disk cache.\n    BuildLog log;\n    if (log.Load(kTestFilename, &err) == LOAD_ERROR) {\n      fprintf(stderr, \"Failed to read test data: %s\\n\", err.c_str());\n      return 1;\n    }\n  }\n  const int kNumRepetitions = 5;\n  for (int i = 0; i < kNumRepetitions; ++i) {\n    int64_t start = GetTimeMillis();\n    BuildLog log;\n    if (log.Load(kTestFilename, &err) == LOAD_ERROR) {\n      fprintf(stderr, \"Failed to read test data: %s\\n\", err.c_str());\n      return 1;\n    }\n    int delta = (int)(GetTimeMillis() - start);\n    printf(\"%dms\\n\", delta);\n    times.push_back(delta);\n  }\n\n  int min = times[0];\n  int max = times[0];\n  float total = 0;\n  for (size_t i = 0; i < times.size(); ++i) {\n    total += times[i];\n    if (times[i] < min)\n      min = times[i];\n    else if (times[i] > max)\n      max = times[i];\n  }\n\n  printf(\"min %dms  max %dms  avg %.1fms\\n\",\n         min, max, total / times.size());\n\n  platformAwareUnlink(kTestFilename);\n\n  return 0;\n}\n"
  },
  {
    "path": "src/build_log_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"build_log.h\"\n\n#include \"util.h\"\n#include \"test.h\"\n\n#include <sys/stat.h>\n#ifdef _WIN32\n#include <fcntl.h>\n#include <share.h>\n#else\n#include <sys/types.h>\n#include <unistd.h>\n#endif\n#include <cassert>\n\nnamespace {\n\nconst char kTestFilename[] = \"BuildLogTest-tempfile\";\n\nstruct BuildLogTest : public StateTestWithBuiltinRules, public BuildLogUser {\n  virtual void SetUp() {\n    // In case a crashing test left a stale file behind.\n    platformAwareUnlink(kTestFilename);\n  }\n  virtual void TearDown() {\n    platformAwareUnlink(kTestFilename);\n  }\n  virtual bool IsPathDead(StringPiece s) const { return false; }\n};\n\nTEST_F(BuildLogTest, WriteRead) {\n  AssertParse(&state_,\n\"build out: cat mid\\n\"\n\"build mid: cat in\\n\");\n\n  BuildLog log1;\n  std::string err;\n  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));\n  ASSERT_EQ(\"\", err);\n  log1.RecordCommand(state_.edges_[0], 15, 18);\n  log1.RecordCommand(state_.edges_[1], 20, 25);\n  log1.Close();\n\n  BuildLog log2;\n  EXPECT_TRUE(log2.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_EQ(2u, log1.entries().size());\n  ASSERT_EQ(2u, log2.entries().size());\n  BuildLog::LogEntry* e1 = log1.LookupByOutput(\"out\");\n  ASSERT_TRUE(e1);\n  BuildLog::LogEntry* e2 = log2.LookupByOutput(\"out\");\n  ASSERT_TRUE(e2);\n  ASSERT_TRUE(*e1 == *e2);\n  ASSERT_EQ(15, e1->start_time);\n  ASSERT_EQ(\"out\", e1->output);\n}\n\nTEST_F(BuildLogTest, FirstWriteAddsSignature) {\n  const char kExpectedVersion[] = \"# ninja log vX\\n\";\n  const size_t kVersionPos = strlen(kExpectedVersion) - 2;  // Points at 'X'.\n\n  BuildLog log;\n  std::string contents, err;\n\n  EXPECT_TRUE(log.OpenForWrite(kTestFilename, *this, &err));\n  ASSERT_EQ(\"\", err);\n  log.Close();\n\n  ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err));\n  ASSERT_EQ(\"\", err);\n  if (contents.size() >= kVersionPos)\n    contents[kVersionPos] = 'X';\n  EXPECT_EQ(kExpectedVersion, contents);\n\n  // Opening the file anew shouldn't add a second version string.\n  EXPECT_TRUE(log.OpenForWrite(kTestFilename, *this, &err));\n  ASSERT_EQ(\"\", err);\n  log.Close();\n\n  contents.clear();\n  ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err));\n  ASSERT_EQ(\"\", err);\n  if (contents.size() >= kVersionPos)\n    contents[kVersionPos] = 'X';\n  EXPECT_EQ(kExpectedVersion, contents);\n}\n\nTEST_F(BuildLogTest, DoubleEntry) {\n  FILE* f = fopen(kTestFilename, \"wb\");\n  fprintf(f, \"# ninja log v7\\n\");\n  fprintf(f, \"0\\t1\\t2\\tout\\t%\" PRIx64 \"\\n\",\n      BuildLog::LogEntry::HashCommand(\"command abc\"));\n  fprintf(f, \"0\\t1\\t2\\tout\\t%\" PRIx64 \"\\n\",\n      BuildLog::LogEntry::HashCommand(\"command def\"));\n  fclose(f);\n\n  std::string err;\n  BuildLog log;\n  EXPECT_TRUE(log.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  BuildLog::LogEntry* e = log.LookupByOutput(\"out\");\n  ASSERT_TRUE(e);\n  ASSERT_NO_FATAL_FAILURE(AssertHash(\"command def\", e->command_hash));\n}\n\nTEST_F(BuildLogTest, Truncate) {\n  AssertParse(&state_,\n\"build out: cat mid\\n\"\n\"build mid: cat in\\n\");\n\n  {\n    BuildLog log1;\n    std::string err;\n    EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));\n    ASSERT_EQ(\"\", err);\n    log1.RecordCommand(state_.edges_[0], 15, 18);\n    log1.RecordCommand(state_.edges_[1], 20, 25);\n    log1.Close();\n  }\n#ifdef __USE_LARGEFILE64\n  struct stat64 statbuf;\n  ASSERT_EQ(0, stat64(kTestFilename, &statbuf));\n#else\n  struct stat statbuf;\n  ASSERT_EQ(0, stat(kTestFilename, &statbuf));\n#endif\n  ASSERT_GT(statbuf.st_size, 0);\n\n  // For all possible truncations of the input file, assert that we don't\n  // crash when parsing.\n  for (off_t size = statbuf.st_size; size > 0; --size) {\n    BuildLog log2;\n    std::string err;\n    EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err));\n    ASSERT_EQ(\"\", err);\n    log2.RecordCommand(state_.edges_[0], 15, 18);\n    log2.RecordCommand(state_.edges_[1], 20, 25);\n    log2.Close();\n\n    ASSERT_TRUE(Truncate(kTestFilename, size, &err));\n\n    BuildLog log3;\n    err.clear();\n    ASSERT_TRUE(log3.Load(kTestFilename, &err) == LOAD_SUCCESS || !err.empty());\n  }\n}\n\nTEST_F(BuildLogTest, ObsoleteOldVersion) {\n  FILE* f = fopen(kTestFilename, \"wb\");\n  fprintf(f, \"# ninja log v3\\n\");\n  fprintf(f, \"123 456 0 out command\\n\");\n  fclose(f);\n\n  std::string err;\n  BuildLog log;\n  EXPECT_TRUE(log.Load(kTestFilename, &err));\n  ASSERT_NE(err.find(\"version\"), std::string::npos);\n}\n\nTEST_F(BuildLogTest, SpacesInOutput) {\n  FILE* f = fopen(kTestFilename, \"wb\");\n  fprintf(f, \"# ninja log v7\\n\");\n  fprintf(f, \"123\\t456\\t456\\tout with space\\t%\" PRIx64 \"\\n\",\n      BuildLog::LogEntry::HashCommand(\"command\"));\n  fclose(f);\n\n  std::string err;\n  BuildLog log;\n  EXPECT_TRUE(log.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  BuildLog::LogEntry* e = log.LookupByOutput(\"out with space\");\n  ASSERT_TRUE(e);\n  ASSERT_EQ(123, e->start_time);\n  ASSERT_EQ(456, e->end_time);\n  ASSERT_EQ(456, e->mtime);\n  ASSERT_NO_FATAL_FAILURE(AssertHash(\"command\", e->command_hash));\n}\n\nTEST_F(BuildLogTest, DuplicateVersionHeader) {\n  // Old versions of ninja accidentally wrote multiple version headers to the\n  // build log on Windows. This shouldn't crash, and the second version header\n  // should be ignored.\n  FILE* f = fopen(kTestFilename, \"wb\");\n  fprintf(f, \"# ninja log v7\\n\");\n  fprintf(f, \"123\\t456\\t456\\tout\\t%\" PRIx64 \"\\n\",\n      BuildLog::LogEntry::HashCommand(\"command\"));\n  fprintf(f, \"# ninja log v7\\n\");\n  fprintf(f, \"456\\t789\\t789\\tout2\\t%\" PRIx64 \"\\n\",\n      BuildLog::LogEntry::HashCommand(\"command2\"));\n  fclose(f);\n\n  std::string err;\n  BuildLog log;\n  EXPECT_TRUE(log.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  BuildLog::LogEntry* e = log.LookupByOutput(\"out\");\n  ASSERT_TRUE(e);\n  ASSERT_EQ(123, e->start_time);\n  ASSERT_EQ(456, e->end_time);\n  ASSERT_EQ(456, e->mtime);\n  ASSERT_NO_FATAL_FAILURE(AssertHash(\"command\", e->command_hash));\n\n  e = log.LookupByOutput(\"out2\");\n  ASSERT_TRUE(e);\n  ASSERT_EQ(456, e->start_time);\n  ASSERT_EQ(789, e->end_time);\n  ASSERT_EQ(789, e->mtime);\n  ASSERT_NO_FATAL_FAILURE(AssertHash(\"command2\", e->command_hash));\n}\n\nstruct TestDiskInterface : public DiskInterface {\n  TimeStamp Stat(const std::string& path, std::string* err) const override {\n    return 4;\n  }\n  bool WriteFile(const std::string& path, const std::string& contents,\n                 bool crlf_on_windows) override {\n    assert(false);\n    return true;\n  }\n  bool MakeDir(const std::string& path) override {\n    assert(false);\n    return false;\n  }\n  Status ReadFile(const std::string& path, std::string* contents,\n                  std::string* err) override {\n    assert(false);\n    return NotFound;\n  }\n  int RemoveFile(const std::string& path) override {\n    assert(false);\n    return 0;\n  }\n};\n\nTEST_F(BuildLogTest, Restat) {\n  FILE* f = fopen(kTestFilename, \"wb\");\n  fprintf(f, \"# ninja log v7\\n\"\n             \"1\\t2\\t3\\tout\\tcommand\\n\");\n  fclose(f);\n  std::string err;\n  BuildLog log;\n  EXPECT_TRUE(log.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n  BuildLog::LogEntry* e = log.LookupByOutput(\"out\");\n  ASSERT_EQ(3, e->mtime);\n\n  TestDiskInterface testDiskInterface;\n  char out2[] = { 'o', 'u', 't', '2', 0 };\n  char* filter2[] = { out2 };\n  EXPECT_TRUE(log.Restat(kTestFilename, testDiskInterface, 1, filter2, &err));\n  ASSERT_EQ(\"\", err);\n  e = log.LookupByOutput(\"out\");\n  ASSERT_EQ(3, e->mtime); // unchanged, since the filter doesn't match\n\n  EXPECT_TRUE(log.Restat(kTestFilename, testDiskInterface, 0, NULL, &err));\n  ASSERT_EQ(\"\", err);\n  e = log.LookupByOutput(\"out\");\n  ASSERT_EQ(4, e->mtime);\n}\n\nTEST_F(BuildLogTest, VeryLongInputLine) {\n  // Ninja's build log buffer is currently 256kB. Lines longer than that are\n  // silently ignored, but don't affect parsing of other lines.\n  FILE* f = fopen(kTestFilename, \"wb\");\n  fprintf(f, \"# ninja log v7\\n\");\n  fprintf(f, \"123\\t456\\t456\\tout\\tcommand start\");\n  for (size_t i = 0; i < (512 << 10) / strlen(\" more_command\"); ++i)\n    fputs(\" more_command\", f);\n  fprintf(f, \"\\n\");\n  fprintf(f, \"456\\t789\\t789\\tout2\\t%\" PRIx64 \"\\n\",\n      BuildLog::LogEntry::HashCommand(\"command2\"));\n  fclose(f);\n\n  std::string err;\n  BuildLog log;\n  EXPECT_TRUE(log.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  BuildLog::LogEntry* e = log.LookupByOutput(\"out\");\n  ASSERT_EQ(NULL, e);\n\n  e = log.LookupByOutput(\"out2\");\n  ASSERT_TRUE(e);\n  ASSERT_EQ(456, e->start_time);\n  ASSERT_EQ(789, e->end_time);\n  ASSERT_EQ(789, e->mtime);\n  ASSERT_NO_FATAL_FAILURE(AssertHash(\"command2\", e->command_hash));\n}\n\nTEST_F(BuildLogTest, MultiTargetEdge) {\n  AssertParse(&state_,\n\"build out out.d: cat\\n\");\n\n  BuildLog log;\n  log.RecordCommand(state_.edges_[0], 21, 22);\n\n  ASSERT_EQ(2u, log.entries().size());\n  BuildLog::LogEntry* e1 = log.LookupByOutput(\"out\");\n  ASSERT_TRUE(e1);\n  BuildLog::LogEntry* e2 = log.LookupByOutput(\"out.d\");\n  ASSERT_TRUE(e2);\n  ASSERT_EQ(\"out\", e1->output);\n  ASSERT_EQ(\"out.d\", e2->output);\n  ASSERT_EQ(21, e1->start_time);\n  ASSERT_EQ(21, e2->start_time);\n  ASSERT_EQ(22, e2->end_time);\n  ASSERT_EQ(22, e2->end_time);\n}\n\nstruct BuildLogRecompactTest : public BuildLogTest {\n  virtual bool IsPathDead(StringPiece s) const { return s == \"out2\"; }\n};\n\nTEST_F(BuildLogRecompactTest, Recompact) {\n  AssertParse(&state_,\n\"build out: cat in\\n\"\n\"build out2: cat in\\n\");\n\n  BuildLog log1;\n  std::string err;\n  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));\n  ASSERT_EQ(\"\", err);\n  // Record the same edge several times, to trigger recompaction\n  // the next time the log is opened.\n  for (int i = 0; i < 200; ++i)\n    log1.RecordCommand(state_.edges_[0], 15, 18 + i);\n  log1.RecordCommand(state_.edges_[1], 21, 22);\n  log1.Close();\n\n  // Load...\n  BuildLog log2;\n  EXPECT_TRUE(log2.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(2u, log2.entries().size());\n  ASSERT_TRUE(log2.LookupByOutput(\"out\"));\n  ASSERT_TRUE(log2.LookupByOutput(\"out2\"));\n  // ...and force a recompaction.\n  EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err));\n  log2.Close();\n\n  // \"out2\" is dead, it should've been removed.\n  BuildLog log3;\n  EXPECT_TRUE(log2.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, log2.entries().size());\n  ASSERT_TRUE(log2.LookupByOutput(\"out\"));\n  ASSERT_FALSE(log2.LookupByOutput(\"out2\"));\n}\n\n}  // anonymous namespace\n"
  },
  {
    "path": "src/build_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"build.h\"\n\n#include <assert.h>\n#include <climits>\n#include <stdint.h>\n\n#include \"build_log.h\"\n#include \"deps_log.h\"\n#include \"exit_status.h\"\n#include \"graph.h\"\n#include \"status_printer.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nstruct CompareEdgesByOutput {\n  static bool cmp(const Edge* a, const Edge* b) {\n    return a->outputs_[0]->path() < b->outputs_[0]->path();\n  }\n};\n\n/// Fixture for tests involving Plan.\n// Though Plan doesn't use State, it's useful to have one around\n// to create Nodes and Edges.\nstruct PlanTest : public StateTestWithBuiltinRules {\n  Plan plan_;\n\n  /// Because FindWork does not return Edges in any sort of predictable order,\n  // provide a means to get available Edges in order and in a format which is\n  // easy to write tests around.\n  void FindWorkSorted(deque<Edge*>* ret, int count) {\n    for (int i = 0; i < count; ++i) {\n      ASSERT_TRUE(plan_.more_to_do());\n      Edge* edge = plan_.FindWork();\n      ASSERT_TRUE(edge);\n      ret->push_back(edge);\n    }\n    ASSERT_FALSE(plan_.FindWork());\n    sort(ret->begin(), ret->end(), CompareEdgesByOutput::cmp);\n  }\n\n  void PrepareForTarget(const char* node, BuildLog *log=NULL) {\n    string err;\n    EXPECT_TRUE(plan_.AddTarget(GetNode(node), &err));\n    ASSERT_EQ(\"\", err);\n    plan_.PrepareQueue();\n    ASSERT_TRUE(plan_.more_to_do());\n  }\n\n  void TestPoolWithDepthOne(const char *test_case);\n};\n\nTEST_F(PlanTest, Basic) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat mid\\n\"\n\"build mid: cat in\\n\"));\n  GetNode(\"mid\")->MarkDirty();\n  GetNode(\"out\")->MarkDirty();\n  PrepareForTarget(\"out\");\n\n  Edge* edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_EQ(\"in\",  edge->inputs_[0]->path());\n  ASSERT_EQ(\"mid\", edge->outputs_[0]->path());\n\n  ASSERT_FALSE(plan_.FindWork());\n\n  string err;\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_EQ(\"mid\", edge->inputs_[0]->path());\n  ASSERT_EQ(\"out\", edge->outputs_[0]->path());\n\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_FALSE(plan_.more_to_do());\n  edge = plan_.FindWork();\n  ASSERT_EQ(0, edge);\n}\n\n// Test that two outputs from one rule can be handled as inputs to the next.\nTEST_F(PlanTest, DoubleOutputDirect) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat mid1 mid2\\n\"\n\"build mid1 mid2: cat in\\n\"));\n  GetNode(\"mid1\")->MarkDirty();\n  GetNode(\"mid2\")->MarkDirty();\n  GetNode(\"out\")->MarkDirty();\n  PrepareForTarget(\"out\");\n\n  Edge* edge;\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat in\n  string err;\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat mid1 mid2\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_FALSE(edge);  // done\n}\n\n// Test that two outputs from one rule can eventually be routed to another.\nTEST_F(PlanTest, DoubleOutputIndirect) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat b1 b2\\n\"\n\"build b1: cat a1\\n\"\n\"build b2: cat a2\\n\"\n\"build a1 a2: cat in\\n\"));\n  GetNode(\"a1\")->MarkDirty();\n  GetNode(\"a2\")->MarkDirty();\n  GetNode(\"b1\")->MarkDirty();\n  GetNode(\"b2\")->MarkDirty();\n  GetNode(\"out\")->MarkDirty();\n  PrepareForTarget(\"out\");\n\n  Edge* edge;\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat in\n  string err;\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat a1\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat a2\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat b1 b2\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_FALSE(edge);  // done\n}\n\n// Test that two edges from one output can both execute.\nTEST_F(PlanTest, DoubleDependent) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat a1 a2\\n\"\n\"build a1: cat mid\\n\"\n\"build a2: cat mid\\n\"\n\"build mid: cat in\\n\"));\n  GetNode(\"mid\")->MarkDirty();\n  GetNode(\"a1\")->MarkDirty();\n  GetNode(\"a2\")->MarkDirty();\n  GetNode(\"out\")->MarkDirty();\n  PrepareForTarget(\"out\");\n\n  Edge* edge;\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat in\n  string err;\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat mid\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat mid\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);  // cat a1 a2\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_FALSE(edge);  // done\n}\n\nvoid PlanTest::TestPoolWithDepthOne(const char* test_case) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, test_case));\n  GetNode(\"out1\")->MarkDirty();\n  GetNode(\"out2\")->MarkDirty();\n  string err;\n  EXPECT_TRUE(plan_.AddTarget(GetNode(\"out1\"), &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(plan_.AddTarget(GetNode(\"out2\"), &err));\n  ASSERT_EQ(\"\", err);\n  plan_.PrepareQueue();\n  ASSERT_TRUE(plan_.more_to_do());\n\n  Edge* edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_EQ(\"in\",  edge->inputs_[0]->path());\n  ASSERT_EQ(\"out1\", edge->outputs_[0]->path());\n\n  // This will be false since poolcat is serialized\n  ASSERT_FALSE(plan_.FindWork());\n\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_EQ(\"in\", edge->inputs_[0]->path());\n  ASSERT_EQ(\"out2\", edge->outputs_[0]->path());\n\n  ASSERT_FALSE(plan_.FindWork());\n\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_FALSE(plan_.more_to_do());\n  edge = plan_.FindWork();\n  ASSERT_EQ(0, edge);\n}\n\nTEST_F(PlanTest, PoolWithDepthOne) {\n  TestPoolWithDepthOne(\n\"pool foobar\\n\"\n\"  depth = 1\\n\"\n\"rule poolcat\\n\"\n\"  command = cat $in > $out\\n\"\n\"  pool = foobar\\n\"\n\"build out1: poolcat in\\n\"\n\"build out2: poolcat in\\n\");\n}\n\nTEST_F(PlanTest, ConsolePool) {\n  TestPoolWithDepthOne(\n\"rule poolcat\\n\"\n\"  command = cat $in > $out\\n\"\n\"  pool = console\\n\"\n\"build out1: poolcat in\\n\"\n\"build out2: poolcat in\\n\");\n}\n\nTEST_F(PlanTest, PoolsWithDepthTwo) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"pool foobar\\n\"\n\"  depth = 2\\n\"\n\"pool bazbin\\n\"\n\"  depth = 2\\n\"\n\"rule foocat\\n\"\n\"  command = cat $in > $out\\n\"\n\"  pool = foobar\\n\"\n\"rule bazcat\\n\"\n\"  command = cat $in > $out\\n\"\n\"  pool = bazbin\\n\"\n\"build out1: foocat in\\n\"\n\"build out2: foocat in\\n\"\n\"build out3: foocat in\\n\"\n\"build outb1: bazcat in\\n\"\n\"build outb2: bazcat in\\n\"\n\"build outb3: bazcat in\\n\"\n\"  pool =\\n\"\n\"build allTheThings: cat out1 out2 out3 outb1 outb2 outb3\\n\"\n));\n  // Mark all the out* nodes dirty\n  for (int i = 0; i < 3; ++i) {\n    GetNode(\"out\" + string(1, '1' + static_cast<char>(i)))->MarkDirty();\n    GetNode(\"outb\" + string(1, '1' + static_cast<char>(i)))->MarkDirty();\n  }\n  GetNode(\"allTheThings\")->MarkDirty();\n  PrepareForTarget(\"allTheThings\");\n\n  deque<Edge*> edges;\n  FindWorkSorted(&edges, 5);\n\n  for (int i = 0; i < 4; ++i) {\n    Edge *edge = edges[i];\n    ASSERT_EQ(\"in\",  edge->inputs_[0]->path());\n    string base_name(i < 2 ? \"out\" : \"outb\");\n    ASSERT_EQ(base_name + string(1, '1' + (i % 2)), edge->outputs_[0]->path());\n  }\n\n  // outb3 is exempt because it has an empty pool\n  Edge* edge = edges[4];\n  ASSERT_TRUE(edge);\n  ASSERT_EQ(\"in\",  edge->inputs_[0]->path());\n  ASSERT_EQ(\"outb3\", edge->outputs_[0]->path());\n\n  // finish out1\n  string err;\n  plan_.EdgeFinished(edges.front(), Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n  edges.pop_front();\n\n  // out3 should be available\n  Edge* out3 = plan_.FindWork();\n  ASSERT_TRUE(out3);\n  ASSERT_EQ(\"in\",  out3->inputs_[0]->path());\n  ASSERT_EQ(\"out3\", out3->outputs_[0]->path());\n\n  ASSERT_FALSE(plan_.FindWork());\n\n  plan_.EdgeFinished(out3, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_FALSE(plan_.FindWork());\n\n  for (deque<Edge*>::iterator it = edges.begin(); it != edges.end(); ++it) {\n    plan_.EdgeFinished(*it, Plan::kEdgeSucceeded, &err);\n    ASSERT_EQ(\"\", err);\n  }\n\n  Edge* last = plan_.FindWork();\n  ASSERT_TRUE(last);\n  ASSERT_EQ(\"allTheThings\", last->outputs_[0]->path());\n\n  plan_.EdgeFinished(last, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_FALSE(plan_.more_to_do());\n  ASSERT_FALSE(plan_.FindWork());\n}\n\nTEST_F(PlanTest, PoolWithRedundantEdges) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"pool compile\\n\"\n    \"  depth = 1\\n\"\n    \"rule gen_foo\\n\"\n    \"  command = touch foo.cpp\\n\"\n    \"rule gen_bar\\n\"\n    \"  command = touch bar.cpp\\n\"\n    \"rule echo\\n\"\n    \"  command = echo $out > $out\\n\"\n    \"build foo.cpp.obj: echo foo.cpp || foo.cpp\\n\"\n    \"  pool = compile\\n\"\n    \"build bar.cpp.obj: echo bar.cpp || bar.cpp\\n\"\n    \"  pool = compile\\n\"\n    \"build libfoo.a: echo foo.cpp.obj bar.cpp.obj\\n\"\n    \"build foo.cpp: gen_foo\\n\"\n    \"build bar.cpp: gen_bar\\n\"\n    \"build all: phony libfoo.a\\n\"));\n  GetNode(\"foo.cpp\")->MarkDirty();\n  GetNode(\"foo.cpp.obj\")->MarkDirty();\n  GetNode(\"bar.cpp\")->MarkDirty();\n  GetNode(\"bar.cpp.obj\")->MarkDirty();\n  GetNode(\"libfoo.a\")->MarkDirty();\n  GetNode(\"all\")->MarkDirty();\n  PrepareForTarget(\"all\");\n\n  Edge* edge = NULL;\n\n  deque<Edge*> initial_edges;\n  FindWorkSorted(&initial_edges, 2);\n\n  edge = initial_edges[1];  // Foo first\n  ASSERT_EQ(\"foo.cpp\", edge->outputs_[0]->path());\n  string err;\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_FALSE(plan_.FindWork());\n  ASSERT_EQ(\"foo.cpp\", edge->inputs_[0]->path());\n  ASSERT_EQ(\"foo.cpp\", edge->inputs_[1]->path());\n  ASSERT_EQ(\"foo.cpp.obj\", edge->outputs_[0]->path());\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = initial_edges[0];  // Now for bar\n  ASSERT_EQ(\"bar.cpp\", edge->outputs_[0]->path());\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_FALSE(plan_.FindWork());\n  ASSERT_EQ(\"bar.cpp\", edge->inputs_[0]->path());\n  ASSERT_EQ(\"bar.cpp\", edge->inputs_[1]->path());\n  ASSERT_EQ(\"bar.cpp.obj\", edge->outputs_[0]->path());\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_FALSE(plan_.FindWork());\n  ASSERT_EQ(\"foo.cpp.obj\", edge->inputs_[0]->path());\n  ASSERT_EQ(\"bar.cpp.obj\", edge->inputs_[1]->path());\n  ASSERT_EQ(\"libfoo.a\", edge->outputs_[0]->path());\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_FALSE(plan_.FindWork());\n  ASSERT_EQ(\"libfoo.a\", edge->inputs_[0]->path());\n  ASSERT_EQ(\"all\", edge->outputs_[0]->path());\n  plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_FALSE(edge);\n  ASSERT_FALSE(plan_.more_to_do());\n}\n\nTEST_F(PlanTest, PoolWithFailingEdge) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"pool foobar\\n\"\n    \"  depth = 1\\n\"\n    \"rule poolcat\\n\"\n    \"  command = cat $in > $out\\n\"\n    \"  pool = foobar\\n\"\n    \"build out1: poolcat in\\n\"\n    \"build out2: poolcat in\\n\"));\n  GetNode(\"out1\")->MarkDirty();\n  GetNode(\"out2\")->MarkDirty();\n  string err;\n  EXPECT_TRUE(plan_.AddTarget(GetNode(\"out1\"), &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(plan_.AddTarget(GetNode(\"out2\"), &err));\n  ASSERT_EQ(\"\", err);\n  plan_.PrepareQueue();\n  ASSERT_TRUE(plan_.more_to_do());\n\n  Edge* edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_EQ(\"in\",  edge->inputs_[0]->path());\n  ASSERT_EQ(\"out1\", edge->outputs_[0]->path());\n\n  // This will be false since poolcat is serialized\n  ASSERT_FALSE(plan_.FindWork());\n\n  plan_.EdgeFinished(edge, Plan::kEdgeFailed, &err);\n  ASSERT_EQ(\"\", err);\n\n  edge = plan_.FindWork();\n  ASSERT_TRUE(edge);\n  ASSERT_EQ(\"in\", edge->inputs_[0]->path());\n  ASSERT_EQ(\"out2\", edge->outputs_[0]->path());\n\n  ASSERT_FALSE(plan_.FindWork());\n\n  plan_.EdgeFinished(edge, Plan::kEdgeFailed, &err);\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_TRUE(plan_.more_to_do()); // Jobs have failed\n  edge = plan_.FindWork();\n  ASSERT_EQ(0, edge);\n}\n\nTEST_F(PlanTest, PriorityWithoutBuildLog) {\n  // Without a build log, the critical time is equivalent to graph\n  // depth. Test with the following graph:\n  //   a2\n  //   |\n  //   a1  b1\n  //   |  |  |\n  //   a0 b0 c0\n  //    \\ | /\n  //     out\n\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"rule r\\n\"\n    \"  command = unused\\n\"\n    \"build out: r a0 b0 c0\\n\"\n    \"build a0: r a1\\n\"\n    \"build a1: r a2\\n\"\n    \"build b0: r b1\\n\"\n    \"build c0: r b1\\n\"\n  ));\n  GetNode(\"a1\")->MarkDirty();\n  GetNode(\"a0\")->MarkDirty();\n  GetNode(\"b0\")->MarkDirty();\n  GetNode(\"c0\")->MarkDirty();\n  GetNode(\"out\")->MarkDirty();\n  BuildLog log;\n  PrepareForTarget(\"out\", &log);\n\n  EXPECT_EQ(GetNode(\"out\")->in_edge()->critical_path_weight(), 1);\n  EXPECT_EQ(GetNode(\"a0\")->in_edge()->critical_path_weight(), 2);\n  EXPECT_EQ(GetNode(\"b0\")->in_edge()->critical_path_weight(), 2);\n  EXPECT_EQ(GetNode(\"c0\")->in_edge()->critical_path_weight(), 2);\n  EXPECT_EQ(GetNode(\"a1\")->in_edge()->critical_path_weight(), 3);\n\n  const int n_edges = 5;\n  const char *expected_order[n_edges] = {\n    \"a1\", \"a0\", \"b0\", \"c0\", \"out\"};\n  for (int i = 0; i < n_edges; ++i) {\n    Edge* edge = plan_.FindWork();\n    ASSERT_TRUE(edge != nullptr);\n    EXPECT_EQ(expected_order[i], edge->outputs_[0]->path());\n\n    std::string err;\n    ASSERT_TRUE(plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, &err));\n    EXPECT_EQ(err, \"\");\n  }\n\n  EXPECT_FALSE(plan_.FindWork());\n}\n\n/// Fake implementation of CommandRunner, useful for tests.\nstruct FakeCommandRunner : public CommandRunner {\n  explicit FakeCommandRunner(VirtualFileSystem* fs) :\n      max_active_edges_(1), fs_(fs) {}\n\n  // CommandRunner impl\n  virtual size_t CanRunMore() const;\n  virtual bool StartCommand(Edge* edge);\n  virtual bool WaitForCommand(Result* result);\n  virtual vector<Edge*> GetActiveEdges();\n  virtual void Abort();\n\n  vector<string> commands_ran_;\n  vector<Edge*> active_edges_;\n  size_t max_active_edges_;\n  VirtualFileSystem* fs_;\n};\n\nclass SafeRelease {\n  public:\n  ~SafeRelease() {\n    builder_->command_runner_.release();\n  }\n  SafeRelease(Builder* builder) {\n    builder_ = builder;\n  }\n  Builder* builder_;\n};\n\nstruct BuildTest : public StateTestWithBuiltinRules, public BuildLogUser {\n  BuildTest() : config_(MakeConfig()), command_runner_(&fs_), status_(config_),\n                builder_(&state_, config_, NULL, NULL, &fs_, &status_, 0) {\n  }\n\n  explicit BuildTest(DepsLog* log)\n      : config_(MakeConfig()), command_runner_(&fs_), status_(config_),\n        builder_(&state_, config_, NULL, log, &fs_, &status_, 0) {}\n\n  virtual void SetUp() {\n    StateTestWithBuiltinRules::SetUp();\n\n    builder_.command_runner_.reset(&command_runner_);\n    AssertParse(&state_,\n\"build cat1: cat in1\\n\"\n\"build cat2: cat in1 in2\\n\"\n\"build cat12: cat cat1 cat2\\n\");\n\n    fs_.Create(\"in1\", \"\");\n    fs_.Create(\"in2\", \"\");\n  }\n\n  ~BuildTest() {\n    builder_.command_runner_.release();\n  }\n\n  virtual bool IsPathDead(StringPiece s) const { return false; }\n\n  /// Rebuild target in the 'working tree' (fs_).\n  /// State of command_runner_ and logs contents (if specified) ARE MODIFIED.\n  /// Handy to check for NOOP builds, and higher-level rebuild tests.\n  void RebuildTarget(const string& target, const char* manifest,\n                     const char* log_path = NULL, const char* deps_path = NULL,\n                     State* state = NULL);\n\n  // Mark a path dirty.\n  void Dirty(const string& path);\n\n  BuildConfig MakeConfig() {\n    BuildConfig config;\n    config.verbosity = BuildConfig::QUIET;\n    return config;\n  }\n\n  BuildConfig config_;\n  FakeCommandRunner command_runner_;\n  VirtualFileSystem fs_;\n  StatusPrinter status_;\n  Builder builder_;\n};\n\nvoid BuildTest::RebuildTarget(const string& target, const char* manifest,\n                              const char* log_path, const char* deps_path,\n                              State* state) {\n  State local_state, *pstate = &local_state;\n  if (state)\n    pstate = state;\n  ASSERT_NO_FATAL_FAILURE(AddCatRule(pstate));\n  AssertParse(pstate, manifest);\n\n  string err;\n  BuildLog build_log, *pbuild_log = NULL;\n  if (log_path) {\n    ASSERT_TRUE(build_log.Load(log_path, &err));\n    ASSERT_TRUE(build_log.OpenForWrite(log_path, *this, &err));\n    ASSERT_EQ(\"\", err);\n    pbuild_log = &build_log;\n  }\n\n  DepsLog deps_log, *pdeps_log = NULL;\n  if (deps_path) {\n    ASSERT_TRUE(deps_log.Load(deps_path, pstate, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_path, &err));\n    ASSERT_EQ(\"\", err);\n    pdeps_log = &deps_log;\n  }\n\n  Builder builder(pstate, config_, pbuild_log, pdeps_log, &fs_, &status_, 0);\n  EXPECT_TRUE(builder.AddTarget(target, &err));\n\n  command_runner_.commands_ran_.clear();\n  builder.command_runner_.reset(&command_runner_);\n  SafeRelease protect(&builder);\n  if (!builder.AlreadyUpToDate()) {\n    ExitStatus build_res = builder.Build(&err);\n    EXPECT_EQ(build_res, ExitSuccess);\n  }\n}\n\nsize_t FakeCommandRunner::CanRunMore() const {\n  if (active_edges_.size() < max_active_edges_)\n    return SIZE_MAX;\n\n  return 0;\n}\n\nbool FakeCommandRunner::StartCommand(Edge* edge) {\n  assert(active_edges_.size() < max_active_edges_);\n  assert(find(active_edges_.begin(), active_edges_.end(), edge)\n         == active_edges_.end());\n  commands_ran_.push_back(edge->EvaluateCommand());\n  if (edge->rule().name() == \"cat\"  ||\n      edge->rule().name() == \"cat_rsp\" ||\n      edge->rule().name() == \"cat_rsp_out\" ||\n      edge->rule().name() == \"cc\" ||\n      edge->rule().name() == \"cp_multi_msvc\" ||\n      edge->rule().name() == \"cp_multi_gcc\" ||\n      edge->rule().name() == \"touch\" ||\n      edge->rule().name() == \"touch-interrupt\" ||\n      edge->rule().name() == \"touch-fail-tick2\") {\n    for (vector<Node*>::iterator out = edge->outputs_.begin();\n         out != edge->outputs_.end(); ++out) {\n      fs_->Create((*out)->path(), \"\");\n    }\n  } else if (edge->rule().name() == \"true\" ||\n             edge->rule().name() == \"fail\" ||\n             edge->rule().name() == \"interrupt\" ||\n             edge->rule().name() == \"console\") {\n    // Don't do anything.\n  } else if (edge->rule().name() == \"cp\") {\n    assert(!edge->inputs_.empty());\n    assert(edge->outputs_.size() == 1);\n    string content;\n    string err;\n    if (fs_->ReadFile(edge->inputs_[0]->path(), &content, &err) ==\n        DiskInterface::Okay)\n      fs_->WriteFile(edge->outputs_[0]->path(), content, false);\n  } else if (edge->rule().name() == \"touch-implicit-dep-out\") {\n    string dep = edge->GetBinding(\"test_dependency\");\n    fs_->Tick();\n    fs_->Create(dep, \"\");\n    fs_->Tick();\n    for (vector<Node*>::iterator out = edge->outputs_.begin();\n         out != edge->outputs_.end(); ++out) {\n      fs_->Create((*out)->path(), \"\");\n    }\n  } else if (edge->rule().name() == \"touch-out-implicit-dep\") {\n    string dep = edge->GetBinding(\"test_dependency\");\n    for (vector<Node*>::iterator out = edge->outputs_.begin();\n         out != edge->outputs_.end(); ++out) {\n      fs_->Create((*out)->path(), \"\");\n    }\n    fs_->Tick();\n    fs_->Create(dep, \"\");\n  } else if (edge->rule().name() == \"generate-depfile\") {\n    string dep = edge->GetBinding(\"test_dependency\");\n    bool touch_dep = edge->GetBindingBool(\"touch_dependency\");\n    string depfile = edge->GetUnescapedDepfile();\n    if (touch_dep) {\n      fs_->Tick();\n      fs_->Create(dep, \"\");\n    }\n    string contents;\n    for (vector<Node*>::iterator out = edge->outputs_.begin();\n         out != edge->outputs_.end(); ++out) {\n      contents += (*out)->path() + \": \" + dep + \"\\n\";\n      fs_->Create((*out)->path(), \"\");\n    }\n    fs_->Create(depfile, contents);\n  } else if (edge->rule().name() == \"long-cc\") {\n    string dep = edge->GetBinding(\"test_dependency\");\n    string depfile = edge->GetUnescapedDepfile();\n    string contents;\n    for (vector<Node*>::iterator out = edge->outputs_.begin();\n        out != edge->outputs_.end(); ++out) {\n      fs_->Tick();\n      fs_->Tick();\n      fs_->Tick();\n      fs_->Create((*out)->path(), \"\");\n      contents += (*out)->path() + \": \" + dep + \"\\n\";\n    }\n    if (!dep.empty() && !depfile.empty())\n      fs_->Create(depfile, contents);\n  } else {\n    printf(\"unknown command\\n\");\n    return false;\n  }\n\n  active_edges_.push_back(edge);\n\n  // Allow tests to control the order by the name of the first output.\n  sort(active_edges_.begin(), active_edges_.end(),\n       CompareEdgesByOutput::cmp);\n\n  return true;\n}\n\nbool FakeCommandRunner::WaitForCommand(Result* result) {\n  if (active_edges_.empty())\n    return false;\n\n  // All active edges were already completed immediately when started,\n  // so we can pick any edge here.  Pick the last edge.  Tests can\n  // control the order of edges by the name of the first output.\n  vector<Edge*>::iterator edge_iter = active_edges_.end() - 1;\n\n  Edge* edge = *edge_iter;\n  result->edge = edge;\n\n  if (edge->rule().name() == \"interrupt\" ||\n      edge->rule().name() == \"touch-interrupt\") {\n    result->status = ExitInterrupted;\n    return true;\n  }\n\n  if (edge->rule().name() == \"console\") {\n    if (edge->use_console())\n      result->status = ExitSuccess;\n    else\n      result->status = ExitFailure;\n    active_edges_.erase(edge_iter);\n    return true;\n  }\n\n  if (edge->rule().name() == \"cp_multi_msvc\") {\n    const std::string prefix = edge->GetBinding(\"msvc_deps_prefix\");\n    for (std::vector<Node*>::iterator in = edge->inputs_.begin();\n         in != edge->inputs_.end(); ++in) {\n      result->output += prefix + (*in)->path() + '\\n';\n    }\n  }\n\n  if (edge->rule().name() == \"fail\" ||\n      (edge->rule().name() == \"touch-fail-tick2\" && fs_->now_ == 2))\n    result->status = ExitFailure;\n  else\n    result->status = ExitSuccess;\n\n  // This rule simulates an external process modifying files while the build command runs.\n  // See TestInputMtimeRaceCondition and TestInputMtimeRaceConditionWithDepFile.\n  // Note: only the first and third time the rule is run per test is the file modified, so\n  // the test can verify that subsequent runs without the race have no work to do.\n  if (edge->rule().name() == \"long-cc\") {\n    string dep = edge->GetBinding(\"test_dependency\");\n    if (fs_->now_ == 4)\n      fs_->files_[dep].mtime = 3;\n    if (fs_->now_ == 10)\n      fs_->files_[dep].mtime = 9;\n  }\n\n  // Provide a way for test cases to verify when an edge finishes that\n  // some other edge is still active.  This is useful for test cases\n  // covering behavior involving multiple active edges.\n  const string& verify_active_edge = edge->GetBinding(\"verify_active_edge\");\n  if (!verify_active_edge.empty()) {\n    bool verify_active_edge_found = false;\n    for (vector<Edge*>::iterator i = active_edges_.begin();\n         i != active_edges_.end(); ++i) {\n      if (!(*i)->outputs_.empty() &&\n          (*i)->outputs_[0]->path() == verify_active_edge) {\n        verify_active_edge_found = true;\n      }\n    }\n    EXPECT_TRUE(verify_active_edge_found);\n  }\n\n  active_edges_.erase(edge_iter);\n  return true;\n}\n\nvector<Edge*> FakeCommandRunner::GetActiveEdges() {\n  return active_edges_;\n}\n\nvoid FakeCommandRunner::Abort() {\n  active_edges_.clear();\n}\n\nvoid BuildTest::Dirty(const string& path) {\n  Node* node = GetNode(path);\n  node->MarkDirty();\n\n  // If it's an input file, mark that we've already stat()ed it and\n  // it's missing.\n  if (!node->in_edge())\n    node->MarkMissing();\n}\n\nTEST_F(BuildTest, NoWork) {\n  string err;\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n}\n\nTEST_F(BuildTest, OneStep) {\n  // Given a dirty target with one ready input,\n  // we should rebuild the target.\n  Dirty(\"cat1\");\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"cat1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(std::vector<std::string>({ \"cat in1 > cat1\" }),\n            command_runner_.commands_ran_);\n}\n\nTEST_F(BuildTest, OneStep2) {\n  // Given a target with one dirty input,\n  // we should rebuild the target.\n  Dirty(\"cat1\");\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"cat1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(std::vector<std::string>({ \"cat in1 > cat1\" }),\n            command_runner_.commands_ran_);\n}\n\nTEST_F(BuildTest, TwoStep) {\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"cat12\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  // Depending on how the pointers work out, we could've ran\n  // the first two commands in either order.\n  EXPECT_TRUE((command_runner_.commands_ran_[0] == \"cat in1 > cat1\" &&\n               command_runner_.commands_ran_[1] == \"cat in1 in2 > cat2\") ||\n              (command_runner_.commands_ran_[1] == \"cat in1 > cat1\" &&\n               command_runner_.commands_ran_[0] == \"cat in1 in2 > cat2\"));\n\n  EXPECT_EQ(\"cat cat1 cat2 > cat12\", command_runner_.commands_ran_[2]);\n\n  fs_.Tick();\n\n  // Modifying in2 requires rebuilding one intermediate file\n  // and the final file.\n  fs_.Create(\"in2\", \"\");\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"cat12\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(\n      std::vector<std::string>({ \"cat in1 > cat1\", \"cat in1 in2 > cat2\",\n                                 \"cat cat1 cat2 > cat12\", \"cat in1 in2 > cat2\",\n                                 \"cat cat1 cat2 > cat12\" }),\n      command_runner_.commands_ran_);\n}\n\nTEST_F(BuildTest, TwoOutputs) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"build out1 out2: touch in.txt\\n\"));\n\n  fs_.Create(\"in.txt\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(std::vector<std::string>({ \"touch out1 out2\" }),\n            command_runner_.commands_ran_);\n}\n\nTEST_F(BuildTest, ImplicitOutput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"build out | out.imp: touch in.txt\\n\"));\n  fs_.Create(\"in.txt\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out.imp\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(std::vector<std::string>({ \"touch out out.imp\" }),\n            command_runner_.commands_ran_);\n}\n\n// Test case from\n//   https://github.com/ninja-build/ninja/issues/148\nTEST_F(BuildTest, MultiOutIn) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"build in1 otherfile: touch in\\n\"\n\"build out: touch in | in1\\n\"));\n\n  fs_.Create(\"in\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in1\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(BuildTest, Chain) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build c2: cat c1\\n\"\n\"build c3: cat c2\\n\"\n\"build c4: cat c3\\n\"\n\"build c5: cat c4\\n\"));\n\n  fs_.Create(\"c1\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"c5\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(4u, command_runner_.commands_ran_.size());\n\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"c5\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  fs_.Tick();\n\n  fs_.Create(\"c3\", \"\");\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"c5\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());  // 3->4, 4->5\n}\n\nTEST_F(BuildTest, MissingInput) {\n  // Input is referenced by build file, but no rule for it.\n  string err;\n  Dirty(\"in1\");\n  EXPECT_FALSE(builder_.AddTarget(\"cat1\", &err));\n  EXPECT_EQ(\"'in1', needed by 'cat1', missing and no known rule to make it\",\n            err);\n}\n\nTEST_F(BuildTest, MissingTarget) {\n  // Target is not referenced by build file.\n  string err;\n  EXPECT_FALSE(builder_.AddTarget(\"meow\", &err));\n  EXPECT_EQ(\"unknown target: 'meow'\", err);\n}\n\nTEST_F(BuildTest, MissingInputTarget) {\n  // Target is a missing input file\n  string err;\n  Dirty(\"in1\");\n  EXPECT_FALSE(builder_.AddTarget(\"in1\", &err));\n  EXPECT_EQ(\"'in1' missing and no known rule to make it\", err);\n}\n\nTEST_F(BuildTest, MakeDirs) {\n  string err;\n\n#ifdef _WIN32\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n                                      \"build subdir\\\\dir2\\\\file: cat in1\\n\"));\n#else\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n                                      \"build subdir/dir2/file: cat in1\\n\"));\n#endif\n  EXPECT_TRUE(builder_.AddTarget(\"subdir/dir2/file\", &err));\n\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(2u, fs_.directories_made_.size());\n  EXPECT_EQ(\"subdir\", fs_.directories_made_[0]);\n  EXPECT_EQ(\"subdir/dir2\", fs_.directories_made_[1]);\n}\n\nTEST_F(BuildTest, DepFileMissing) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n  command = cc $in\\n  depfile = $out.d\\n\"\n\"build fo$ o.o: cc foo.c\\n\"));\n  fs_.Create(\"foo.c\", \"\");\n\n  EXPECT_TRUE(builder_.AddTarget(\"fo o.o\", &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, fs_.files_read_.size());\n  EXPECT_EQ(\"fo o.o.d\", fs_.files_read_[0]);\n}\n\nTEST_F(BuildTest, DepFileOK) {\n  string err;\n  int orig_edges = static_cast<int>(state_.edges_.size());\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n  command = cc $in\\n  depfile = $out.d\\n\"\n\"build foo.o: cc foo.c\\n\"));\n  Edge* edge = state_.edges_.back();\n\n  fs_.Create(\"foo.c\", \"\");\n  GetNode(\"bar.h\")->MarkDirty();  // Mark bar.h as missing.\n  fs_.Create(\"foo.o.d\", \"foo.o: blah.h bar.h\\n\");\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, fs_.files_read_.size());\n  EXPECT_EQ(\"foo.o.d\", fs_.files_read_[0]);\n\n  // Expect one new edge generating foo.o. Loading the depfile should have\n  // added nodes, but not phony edges to the graph.\n  ASSERT_EQ(orig_edges + 1, (int)state_.edges_.size());\n\n  // Verify that nodes for blah.h and bar.h were added and that they\n  // are marked as generated by a dep loader.\n  ASSERT_FALSE(state_.LookupNode(\"foo.o\")->generated_by_dep_loader());\n  ASSERT_FALSE(state_.LookupNode(\"foo.c\")->generated_by_dep_loader());\n  ASSERT_TRUE(state_.LookupNode(\"blah.h\"));\n  ASSERT_TRUE(state_.LookupNode(\"blah.h\")->generated_by_dep_loader());\n  ASSERT_TRUE(state_.LookupNode(\"bar.h\"));\n  ASSERT_TRUE(state_.LookupNode(\"bar.h\")->generated_by_dep_loader());\n\n  // Expect our edge to now have three inputs: foo.c and two headers.\n  ASSERT_EQ(3u, edge->inputs_.size());\n\n  // Expect the command line we generate to only use the original input.\n  ASSERT_EQ(\"cc foo.c\", edge->EvaluateCommand());\n}\n\nTEST_F(BuildTest, DepFileParseError) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n  command = cc $in\\n  depfile = $out.d\\n\"\n\"build foo.o: cc foo.c\\n\"));\n  fs_.Create(\"foo.c\", \"\");\n  fs_.Create(\"foo.o.d\", \"randomtext\\n\");\n  EXPECT_FALSE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(\"foo.o.d: expected ':' in depfile\", err);\n}\n\nTEST_F(BuildTest, EncounterReadyTwice) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"build c: touch\\n\"\n\"build b: touch || c\\n\"\n\"build a: touch | b || c\\n\"));\n\n  vector<Edge*> c_out = GetNode(\"c\")->out_edges();\n  ASSERT_EQ(2u, c_out.size());\n  EXPECT_EQ(\"b\", c_out[0]->outputs_[0]->path());\n  EXPECT_EQ(\"a\", c_out[1]->outputs_[0]->path());\n\n  fs_.Create(\"b\", \"\");\n  EXPECT_TRUE(builder_.AddTarget(\"a\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, OrderOnlyDeps) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n  command = cc $in\\n  depfile = $out.d\\n\"\n\"build foo.o: cc foo.c || otherfile\\n\"));\n  Edge* edge = state_.edges_.back();\n\n  fs_.Create(\"foo.c\", \"\");\n  fs_.Create(\"otherfile\", \"\");\n  fs_.Create(\"foo.o.d\", \"foo.o: blah.h bar.h\\n\");\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  ASSERT_EQ(\"\", err);\n\n  // One explicit, two implicit, one order only.\n  ASSERT_EQ(4u, edge->inputs_.size());\n  EXPECT_EQ(2, edge->implicit_deps_);\n  EXPECT_EQ(1, edge->order_only_deps_);\n  // Verify the inputs are in the order we expect\n  // (explicit then implicit then orderonly).\n  EXPECT_EQ(\"foo.c\", edge->inputs_[0]->path());\n  EXPECT_EQ(\"blah.h\", edge->inputs_[1]->path());\n  EXPECT_EQ(\"bar.h\", edge->inputs_[2]->path());\n  EXPECT_EQ(\"otherfile\", edge->inputs_[3]->path());\n\n  // Expect the command line we generate to only use the original input.\n  ASSERT_EQ(\"cc foo.c\", edge->EvaluateCommand());\n\n  // explicit dep dirty, expect a rebuild.\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n  fs_.Tick();\n\n  // Recreate the depfile, as it should have been deleted by the build.\n  fs_.Create(\"foo.o.d\", \"foo.o: blah.h bar.h\\n\");\n\n  // implicit dep dirty, expect a rebuild.\n  fs_.Create(\"blah.h\", \"\");\n  fs_.Create(\"bar.h\", \"\");\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n  fs_.Tick();\n\n  // Recreate the depfile, as it should have been deleted by the build.\n  fs_.Create(\"foo.o.d\", \"foo.o: blah.h bar.h\\n\");\n\n  // order only dep dirty, no rebuild.\n  fs_.Create(\"otherfile\", \"\");\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  // implicit dep missing, expect rebuild.\n  fs_.RemoveFile(\"bar.h\");\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, RebuildOrderOnlyDeps) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n  command = cc $in\\n\"\n\"rule true\\n  command = true\\n\"\n\"build oo.h: cc oo.h.in\\n\"\n\"build foo.o: cc foo.c || oo.h\\n\"));\n\n  fs_.Create(\"foo.c\", \"\");\n  fs_.Create(\"oo.h.in\", \"\");\n\n  // foo.o and order-only dep dirty, build both.\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // all clean, no rebuild.\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  // order-only dep missing, build it only.\n  fs_.RemoveFile(\"oo.h\");\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  ASSERT_EQ(\"cc oo.h.in\", command_runner_.commands_ran_[0]);\n\n  fs_.Tick();\n\n  // order-only dep dirty, build it only.\n  fs_.Create(\"oo.h.in\", \"\");\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"foo.o\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  ASSERT_EQ(\"cc oo.h.in\", command_runner_.commands_ran_[0]);\n}\n\n#ifdef _WIN32\nTEST_F(BuildTest, DepFileCanonicalize) {\n  string err;\n  int orig_edges = state_.edges_.size();\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n  command = cc $in\\n  depfile = $out.d\\n\"\n\"build gen/stuff\\\\things/foo.o: cc x\\\\y/z\\\\foo.c\\n\"));\n\n  fs_.Create(\"x/y/z/foo.c\", \"\");\n  GetNode(\"bar.h\")->MarkDirty();  // Mark bar.h as missing.\n  // Note, different slashes from manifest.\n  fs_.Create(\"gen/stuff\\\\things/foo.o.d\",\n             \"gen\\\\stuff\\\\things\\\\foo.o: blah.h bar.h\\n\");\n  EXPECT_TRUE(builder_.AddTarget(\"gen/stuff/things/foo.o\", &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, fs_.files_read_.size());\n  // The depfile path does not get Canonicalize as it seems unnecessary.\n  EXPECT_EQ(\"gen/stuff\\\\things/foo.o.d\", fs_.files_read_[0]);\n\n  // Expect one new edge enerating foo.o.\n  ASSERT_EQ(orig_edges + 1, (int)state_.edges_.size());\n  // Expect our edge to now have three inputs: foo.c and two headers.\n  Edge* edge = state_.edges_.back();\n  ASSERT_EQ(3u, edge->inputs_.size());\n\n  // Expect the command line we generate to only use the original input, and\n  // using the slashes from the manifest.\n  ASSERT_EQ(\"cc x\\\\y/z\\\\foo.c\", edge->EvaluateCommand());\n}\n#endif\n\nTEST_F(BuildTest, Phony) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat bar.cc\\n\"\n\"build all: phony out\\n\"));\n  fs_.Create(\"bar.cc\", \"\");\n\n  EXPECT_TRUE(builder_.AddTarget(\"all\", &err));\n  ASSERT_EQ(\"\", err);\n\n  // Only one command to run, because phony runs no command.\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, PhonyNoWork) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat bar.cc\\n\"\n\"build all: phony out\\n\"));\n  fs_.Create(\"bar.cc\", \"\");\n  fs_.Create(\"out\", \"\");\n\n  EXPECT_TRUE(builder_.AddTarget(\"all\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n}\n\n// Test a self-referencing phony.  Ideally this should not work, but\n// ninja 1.7 and below tolerated and CMake 2.8.12.x and 3.0.x both\n// incorrectly produce it.  We tolerate it for compatibility.\nTEST_F(BuildTest, PhonySelfReference) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build a: phony a\\n\"));\n\n  EXPECT_TRUE(builder_.AddTarget(\"a\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n}\n\n// There are 6 different cases for phony rules:\n//\n// 1. output edge does not exist, inputs are not real\n// 2. output edge does not exist, no inputs\n// 3. output edge does not exist, inputs are real, newest mtime is M\n// 4. output edge is real, inputs are not real\n// 5. output edge is real, no inputs\n// 6. output edge is real, inputs are real, newest mtime is M\n//\n// Expected results :\n// 1. Edge is marked as clean, mtime is newest mtime of dependents.\n//     Touching inputs will cause dependents to rebuild.\n// 2. Edge is marked as dirty, causing dependent edges to always rebuild\n// 3. Edge is marked as clean, mtime is newest mtime of dependents.\n//     Touching inputs will cause dependents to rebuild.\n// 4. Edge is marked as clean, mtime is newest mtime of dependents.\n//     Touching inputs will cause dependents to rebuild.\n// 5. Edge is marked as dirty, causing dependent edges to always rebuild\n// 6. Edge is marked as clean, mtime is newest mtime of dependents.\n//     Touching inputs will cause dependents to rebuild.\nvoid TestPhonyUseCase(BuildTest* t, int i) {\n  State& state_ = t->state_;\n  Builder& builder_ = t->builder_;\n  FakeCommandRunner& command_runner_ = t->command_runner_;\n  VirtualFileSystem& fs_ = t->fs_;\n\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\" command = touch $out\\n\"\n\"build notreal: phony blank\\n\"\n\"build phony1: phony notreal\\n\"\n\"build phony2: phony\\n\"\n\"build phony3: phony blank\\n\"\n\"build phony4: phony notreal\\n\"\n\"build phony5: phony\\n\"\n\"build phony6: phony blank\\n\"\n\"\\n\"\n\"build test1: touch phony1\\n\"\n\"build test2: touch phony2\\n\"\n\"build test3: touch phony3\\n\"\n\"build test4: touch phony4\\n\"\n\"build test5: touch phony5\\n\"\n\"build test6: touch phony6\\n\"\n));\n\n  // Set up test.\n  builder_.command_runner_.release(); // BuildTest owns the CommandRunner\n  builder_.command_runner_.reset(&command_runner_);\n\n  fs_.Create(\"blank\", \"\");  // a \"real\" file\n  EXPECT_TRUE(builder_.AddTarget(\"test1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AddTarget(\"test2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AddTarget(\"test3\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AddTarget(\"test4\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AddTarget(\"test5\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AddTarget(\"test6\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n\n  string ci;\n  ci += static_cast<char>('0' + i);\n\n  // Tests 1, 3, 4, and 6 should rebuild when the input is updated.\n  if (i != 2 && i != 5) {\n    Node* testNode  = t->GetNode(\"test\" + ci);\n    Node* phonyNode = t->GetNode(\"phony\" + ci);\n    Node* inputNode = t->GetNode(\"blank\");\n\n    state_.Reset();\n    TimeStamp startTime = fs_.now_;\n\n    // Build number 1\n    EXPECT_TRUE(builder_.AddTarget(\"test\" + ci, &err));\n    ASSERT_EQ(\"\", err);\n    if (!builder_.AlreadyUpToDate()) {\n      EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n    }\n    ASSERT_EQ(\"\", err);\n\n    // Touch the input file\n    state_.Reset();\n    command_runner_.commands_ran_.clear();\n    fs_.Tick();\n    fs_.Create(\"blank\", \"\");  // a \"real\" file\n    EXPECT_TRUE(builder_.AddTarget(\"test\" + ci, &err));\n    ASSERT_EQ(\"\", err);\n\n    // Second build, expect testN edge to be rebuilt\n    // and phonyN node's mtime to be updated.\n    EXPECT_FALSE(builder_.AlreadyUpToDate());\n    EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n    ASSERT_EQ(\"\", err);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n    EXPECT_EQ(string(\"touch test\") + ci, command_runner_.commands_ran_[0]);\n    EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n    TimeStamp inputTime = inputNode->mtime();\n\n    EXPECT_FALSE(phonyNode->exists());\n    EXPECT_FALSE(phonyNode->dirty());\n\n    EXPECT_GT(phonyNode->mtime(), startTime);\n    EXPECT_EQ(phonyNode->mtime(), inputTime);\n    ASSERT_TRUE(testNode->Stat(&fs_, &err));\n    EXPECT_TRUE(testNode->exists());\n    EXPECT_GT(testNode->mtime(), startTime);\n  } else {\n    // Tests 2 and 5: Expect dependents to always rebuild.\n\n    state_.Reset();\n    command_runner_.commands_ran_.clear();\n    fs_.Tick();\n    command_runner_.commands_ran_.clear();\n    EXPECT_TRUE(builder_.AddTarget(\"test\" + ci, &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_FALSE(builder_.AlreadyUpToDate());\n    EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n    ASSERT_EQ(\"\", err);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n    EXPECT_EQ(\"touch test\" + ci, command_runner_.commands_ran_[0]);\n\n    state_.Reset();\n    command_runner_.commands_ran_.clear();\n    EXPECT_TRUE(builder_.AddTarget(\"test\" + ci, &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_FALSE(builder_.AlreadyUpToDate());\n    EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n    ASSERT_EQ(\"\", err);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n    EXPECT_EQ(\"touch test\" + ci, command_runner_.commands_ran_[0]);\n  }\n}\n\nTEST_F(BuildTest, PhonyUseCase1) { TestPhonyUseCase(this, 1); }\nTEST_F(BuildTest, PhonyUseCase2) { TestPhonyUseCase(this, 2); }\nTEST_F(BuildTest, PhonyUseCase3) { TestPhonyUseCase(this, 3); }\nTEST_F(BuildTest, PhonyUseCase4) { TestPhonyUseCase(this, 4); }\nTEST_F(BuildTest, PhonyUseCase5) { TestPhonyUseCase(this, 5); }\nTEST_F(BuildTest, PhonyUseCase6) { TestPhonyUseCase(this, 6); }\n\nTEST_F(BuildTest, Fail) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule fail\\n\"\n\"  command = fail\\n\"\n\"build out1: fail\\n\"));\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  ASSERT_EQ(\"subcommand failed\", err);\n}\n\nTEST_F(BuildTest, SwallowFailures) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule fail\\n\"\n\"  command = fail\\n\"\n\"build out1: fail\\n\"\n\"build out2: fail\\n\"\n\"build out3: fail\\n\"\n\"build all: phony out1 out2 out3\\n\"));\n\n  // Swallow two failures, die on the third.\n  config_.failures_allowed = 3;\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"all\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  ASSERT_EQ(\"subcommands failed\", err);\n}\n\nTEST_F(BuildTest, SwallowFailuresLimit) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule fail\\n\"\n\"  command = fail\\n\"\n\"build out1: fail\\n\"\n\"build out2: fail\\n\"\n\"build out3: fail\\n\"\n\"build final: cat out1 out2 out3\\n\"));\n\n  // Swallow ten failures; we should stop before building final.\n  config_.failures_allowed = 11;\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"final\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  ASSERT_EQ(\"cannot make progress due to previous errors\", err);\n}\n\nTEST_F(BuildTest, SwallowFailuresPool) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"pool failpool\\n\"\n\"  depth = 1\\n\"\n\"rule fail\\n\"\n\"  command = fail\\n\"\n\"  pool = failpool\\n\"\n\"build out1: fail\\n\"\n\"build out2: fail\\n\"\n\"build out3: fail\\n\"\n\"build final: cat out1 out2 out3\\n\"));\n\n  // Swallow ten failures; we should stop before building final.\n  config_.failures_allowed = 11;\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"final\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  ASSERT_EQ(\"cannot make progress due to previous errors\", err);\n}\n\nTEST_F(BuildTest, PoolEdgesReadyButNotWanted) {\n  fs_.Create(\"x\", \"\");\n\n  const char* manifest =\n    \"pool some_pool\\n\"\n    \"  depth = 4\\n\"\n    \"rule touch\\n\"\n    \"  command = touch $out\\n\"\n    \"  pool = some_pool\\n\"\n    \"rule cc\\n\"\n    \"  command = touch grit\\n\"\n    \"\\n\"\n    \"build B.d.stamp: cc | x\\n\"\n    \"build C.stamp: touch B.d.stamp\\n\"\n    \"build final.stamp: touch || C.stamp\\n\";\n\n  RebuildTarget(\"final.stamp\", manifest);\n\n  fs_.RemoveFile(\"B.d.stamp\");\n\n  State save_state;\n  RebuildTarget(\"final.stamp\", manifest, NULL, NULL, &save_state);\n  EXPECT_GE(save_state.LookupPool(\"some_pool\")->current_use(), 0);\n}\n\nstruct BuildWithLogTest : public BuildTest {\n  BuildWithLogTest() {\n    builder_.SetBuildLog(&build_log_);\n  }\n\n  BuildLog build_log_;\n};\n\nTEST_F(BuildWithLogTest, ImplicitGeneratedOutOfDate) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"  generator = 1\\n\"\n\"build out.imp: touch | in\\n\"));\n  fs_.Create(\"out.imp\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  string err;\n\n  EXPECT_TRUE(builder_.AddTarget(\"out.imp\", &err));\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n\n  EXPECT_TRUE(GetNode(\"out.imp\")->dirty());\n}\n\nTEST_F(BuildWithLogTest, ImplicitGeneratedOutOfDate2) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch-implicit-dep-out\\n\"\n\"  command = sleep 1 ; touch $test_dependency ; sleep 1 ; touch $out\\n\"\n\"  generator = 1\\n\"\n\"build out.imp: touch-implicit-dep-out | inimp inimp2\\n\"\n\"  test_dependency = inimp\\n\"));\n  fs_.Create(\"inimp\", \"\");\n  fs_.Create(\"out.imp\", \"\");\n  fs_.Tick();\n  fs_.Create(\"inimp2\", \"\");\n  fs_.Tick();\n\n  string err;\n\n  EXPECT_TRUE(builder_.AddTarget(\"out.imp\", &err));\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  builder_.Cleanup();\n  builder_.plan_.Reset();\n\n  EXPECT_TRUE(builder_.AddTarget(\"out.imp\", &err));\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n  EXPECT_FALSE(GetNode(\"out.imp\")->dirty());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  builder_.Cleanup();\n  builder_.plan_.Reset();\n\n  fs_.Tick();\n  fs_.Create(\"inimp\", \"\");\n\n  EXPECT_TRUE(builder_.AddTarget(\"out.imp\", &err));\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  builder_.Cleanup();\n  builder_.plan_.Reset();\n\n  EXPECT_TRUE(builder_.AddTarget(\"out.imp\", &err));\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n  EXPECT_FALSE(GetNode(\"out.imp\")->dirty());\n}\n\nTEST_F(BuildWithLogTest, NotInLogButOnDisk) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n\"\n\"  command = cc\\n\"\n\"build out1: cc in\\n\"));\n\n  // Create input/output that would be considered up to date when\n  // not considering the command line hash.\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out1\", \"\");\n  string err;\n\n  // Because it's not in the log, it should not be up-to-date until\n  // we build again.\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n}\n\nTEST_F(BuildWithLogTest, RebuildAfterFailure) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch-fail-tick2\\n\"\n\"  command = touch-fail-tick2\\n\"\n\"build out1: touch-fail-tick2 in\\n\"));\n\n  string err;\n\n  fs_.Create(\"in\", \"\");\n\n  // Run once successfully to get out1 in the log\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  builder_.Cleanup();\n  builder_.plan_.Reset();\n\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  // Run again with a failure that updates the output file timestamp\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  EXPECT_EQ(\"subcommand failed\", err);\n  EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  builder_.Cleanup();\n  builder_.plan_.Reset();\n\n  fs_.Tick();\n\n  // Run again, should rerun even though the output file is up to date on disk\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(BuildWithLogTest, RebuildWithNoInputs) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch\\n\"\n\"build out1: touch\\n\"\n\"build out2: touch in\\n\"));\n\n  string err;\n\n  fs_.Create(\"in\", \"\");\n\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(2u, command_runner_.commands_ran_.size());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n\n  fs_.Tick();\n\n  fs_.Create(\"in\", \"\");\n\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildWithLogTest, RestatTest) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule true\\n\"\n\"  command = true\\n\"\n\"  restat = 1\\n\"\n\"rule cc\\n\"\n\"  command = cc\\n\"\n\"  restat = 1\\n\"\n\"build out1: cc in\\n\"\n\"build out2: true out1\\n\"\n\"build out3: cat out2\\n\"));\n\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Create(\"out3\", \"\");\n\n  fs_.Tick();\n\n  fs_.Create(\"in\", \"\");\n\n  // Do a pre-build so that there's commands in the log for the outputs,\n  // otherwise, the lack of an entry in the build log will cause out3 to rebuild\n  // regardless of restat.\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out3\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(size_t(3), command_runner_.commands_ran_.size());\n  EXPECT_EQ(3, builder_.plan_.command_edge_count());\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n\n  fs_.Tick();\n\n  fs_.Create(\"in\", \"\");\n  // \"cc\" touches out1, so we should build out2.  But because \"true\" does not\n  // touch out2, we should cancel the build of out3.\n  EXPECT_TRUE(builder_.AddTarget(\"out3\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // If we run again, it should be a no-op, because the build log has recorded\n  // that we've already built out2 with an input timestamp of 2 (from out1).\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out3\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  fs_.Tick();\n\n  fs_.Create(\"in\", \"\");\n\n  // The build log entry should not, however, prevent us from rebuilding out2\n  // if out1 changes.\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out3\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildWithLogTest, RestatMissingFile) {\n  // If a restat rule doesn't create its output, and the output didn't\n  // exist before the rule was run, consider that behavior equivalent\n  // to a rule that doesn't modify its existent output file.\n\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule true\\n\"\n\"  command = true\\n\"\n\"  restat = 1\\n\"\n\"rule cc\\n\"\n\"  command = cc\\n\"\n\"build out1: true in\\n\"\n\"build out2: cc out1\\n\"));\n\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  // Do a pre-build so that there's commands in the log for the outputs,\n  // otherwise, the lack of an entry in the build log will cause out2 to rebuild\n  // regardless of restat.\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  // Run a build, expect only the first command to run.\n  // It doesn't touch its output (due to being the \"true\" command), so\n  // we shouldn't run the dependent build.\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildWithLogTest, RestatSingleDependentOutputDirty) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"rule true\\n\"\n    \"  command = true\\n\"\n    \"  restat = 1\\n\"\n    \"rule touch\\n\"\n    \"  command = touch\\n\"\n    \"build out1: true in\\n\"\n    \"build out2 out3: touch out1\\n\"\n    \"build out4: touch out2\\n\"\n    ));\n\n  // Create the necessary files\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out4\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n  fs_.RemoveFile(\"out3\");\n\n  // Since \"in\" is missing, out1 will be built. Since \"out3\" is missing,\n  // out2 and out3 will be built even though \"in\" is not touched when built.\n  // Then, since out2 is rebuilt, out4 should be rebuilt -- the restat on the\n  // \"true\" rule should not lead to the \"touch\" edge writing out2 and out3 being\n  // cleared.\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out4\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n}\n\n// Test scenario, in which an input file is removed, but output isn't changed\n// https://github.com/ninja-build/ninja/issues/295\nTEST_F(BuildWithLogTest, RestatMissingInput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"rule true\\n\"\n    \"  command = true\\n\"\n    \"  depfile = $out.d\\n\"\n    \"  restat = 1\\n\"\n    \"rule cc\\n\"\n    \"  command = cc\\n\"\n    \"build out1: true in\\n\"\n    \"build out2: cc out1\\n\"));\n\n  // Create all necessary files\n  fs_.Create(\"in\", \"\");\n\n  // The implicit dependencies and the depfile itself\n  // are newer than the output\n  TimeStamp restat_mtime = fs_.Tick();\n  fs_.Create(\"out1.d\", \"out1: will.be.deleted restat.file\\n\");\n  fs_.Create(\"will.be.deleted\", \"\");\n  fs_.Create(\"restat.file\", \"\");\n\n  // Run the build, out1 and out2 get built\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // See that an entry in the logfile is created, capturing\n  // the right mtime\n  BuildLog::LogEntry* log_entry = build_log_.LookupByOutput(\"out1\");\n  ASSERT_TRUE(NULL != log_entry);\n  ASSERT_EQ(restat_mtime, log_entry->mtime);\n\n  // Now remove a file, referenced from depfile, so that target becomes\n  // dirty, but the output does not change\n  fs_.RemoveFile(\"will.be.deleted\");\n\n  // Trigger the build again - only out1 gets built\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n  // Check that the logfile entry remains correctly set\n  log_entry = build_log_.LookupByOutput(\"out1\");\n  ASSERT_TRUE(NULL != log_entry);\n  ASSERT_EQ(restat_mtime, log_entry->mtime);\n}\n\nTEST_F(BuildWithLogTest, RestatInputChangesDueToRule) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule generate-depfile\\n\"\n\"  command = sleep 1 ; touch $touch_dependency; touch $out ; echo \\\"$out: $test_dependency\\\" > $depfile\\n\"\n\"build out1: generate-depfile || cat1\\n\"\n\"  test_dependency = in2\\n\"\n\"  touch_dependency = 1\\n\"\n\"  restat = 1\\n\"\n\"  depfile = out.d\\n\"));\n\n  // Perform the first build. out1 is a restat rule, so its recorded mtime in the build\n  // log should be the time the command completes, not the time the command started. One\n  // of out1's discovered dependencies will have a newer mtime than when out1 started\n  // running, due to its command touching the dependency itself.\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(size_t(2), command_runner_.commands_ran_.size());\n  EXPECT_EQ(2, builder_.plan_.command_edge_count());\n  BuildLog::LogEntry* log_entry = build_log_.LookupByOutput(\"out1\");\n  ASSERT_TRUE(NULL != log_entry);\n  ASSERT_EQ(2u, log_entry->mtime);\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  builder_.Cleanup();\n  builder_.plan_.Reset();\n\n  fs_.Tick();\n  fs_.Create(\"in1\", \"\");\n\n  // Touching a dependency of an order-only dependency of out1 should not cause out1 to\n  // rebuild. If out1 were not a restat rule, then it would rebuild here because its\n  // recorded mtime would have been an earlier mtime than its most recent input's (in2)\n  // mtime\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(!state_.GetNode(\"out1\", 0)->dirty());\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(size_t(1), command_runner_.commands_ran_.size());\n  EXPECT_EQ(1, builder_.plan_.command_edge_count());\n}\n\nTEST_F(BuildWithLogTest, GeneratedPlainDepfileMtime) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule generate-depfile\\n\"\n\"  command = touch $out ; echo \\\"$out: $test_dependency\\\" > $depfile\\n\"\n\"build out: generate-depfile\\n\"\n\"  test_dependency = inimp\\n\"\n\"  depfile = out.d\\n\"));\n  fs_.Create(\"inimp\", \"\");\n  fs_.Tick();\n\n  string err;\n\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  builder_.Cleanup();\n  builder_.plan_.Reset();\n\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n}\n\nstruct BuildDryRun : public BuildWithLogTest {\n  BuildDryRun() {\n    config_.dry_run = true;\n  }\n};\n\nTEST_F(BuildDryRun, AllCommandsShown) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule true\\n\"\n\"  command = true\\n\"\n\"  restat = 1\\n\"\n\"rule cc\\n\"\n\"  command = cc\\n\"\n\"  restat = 1\\n\"\n\"build out1: cc in\\n\"\n\"build out2: true out1\\n\"\n\"build out3: cat out2\\n\"));\n\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Create(\"out3\", \"\");\n\n  fs_.Tick();\n\n  fs_.Create(\"in\", \"\");\n\n  // \"cc\" touches out1, so we should build out2.  But because \"true\" does not\n  // touch out2, we should cancel the build of out3.\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out3\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildDryRun, WithDyndep) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out-copy: cp out\\n\"\n));\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out-copy\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n}\n\n// Test that RSP files are created when & where appropriate and deleted after\n// successful execution.\nTEST_F(BuildTest, RspFileSuccess)\n{\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"rule cat_rsp\\n\"\n    \"  command = cat $rspfile > $out\\n\"\n    \"  rspfile = $rspfile\\n\"\n    \"  rspfile_content = $long_command\\n\"\n    \"rule cat_rsp_out\\n\"\n    \"  command = cat $rspfile > $out\\n\"\n    \"  rspfile = $out.rsp\\n\"\n    \"  rspfile_content = $long_command\\n\"\n    \"build out1: cat in\\n\"\n    \"build out2: cat_rsp in\\n\"\n    \"  rspfile = out 2.rsp\\n\"\n    \"  long_command = Some very long command\\n\"\n    \"build out$ 3: cat_rsp_out in\\n\"\n    \"  long_command = Some very long command\\n\"));\n\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Create(\"out 3\", \"\");\n\n  fs_.Tick();\n\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AddTarget(\"out 3\", &err));\n  ASSERT_EQ(\"\", err);\n\n  size_t files_created = fs_.files_created_.size();\n  size_t files_removed = fs_.files_removed_.size();\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n\n  // The RSP files and temp file to acquire output mtimes were created\n  ASSERT_EQ(files_created + 3, fs_.files_created_.size());\n  ASSERT_EQ(1u, fs_.files_created_.count(\"out 2.rsp\"));\n  ASSERT_EQ(1u, fs_.files_created_.count(\"out 3.rsp\"));\n  ASSERT_EQ(1u, fs_.files_created_.count(\".ninja_lock\"));\n\n  // The RSP files were removed\n  ASSERT_EQ(files_removed + 2, fs_.files_removed_.size());\n  ASSERT_EQ(1u, fs_.files_removed_.count(\"out 2.rsp\"));\n  ASSERT_EQ(1u, fs_.files_removed_.count(\"out 3.rsp\"));\n}\n\n// Test that RSP file is created but not removed for commands, which fail\nTEST_F(BuildTest, RspFileFailure) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"rule fail\\n\"\n    \"  command = fail\\n\"\n    \"  rspfile = $rspfile\\n\"\n    \"  rspfile_content = $long_command\\n\"\n    \"build out: fail in\\n\"\n    \"  rspfile = out.rsp\\n\"\n    \"  long_command = Another very long command\\n\"));\n\n  fs_.Create(\"out\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  size_t files_created = fs_.files_created_.size();\n  size_t files_removed = fs_.files_removed_.size();\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  ASSERT_EQ(\"subcommand failed\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n  // The RSP file and temp file to acquire output mtimes were created\n  ASSERT_EQ(files_created + 2, fs_.files_created_.size());\n  ASSERT_EQ(1u, fs_.files_created_.count(\"out.rsp\"));\n  ASSERT_EQ(1u, fs_.files_created_.count(\".ninja_lock\"));\n\n  // The RSP file was NOT removed\n  ASSERT_EQ(files_removed, fs_.files_removed_.size());\n  ASSERT_EQ(0u, fs_.files_removed_.count(\"out.rsp\"));\n\n  // The RSP file contains what it should\n  ASSERT_EQ(\"Another very long command\", fs_.files_[\"out.rsp\"].contents);\n}\n\n// Test that contents of the RSP file behaves like a regular part of\n// command line, i.e. triggers a rebuild if changed\nTEST_F(BuildWithLogTest, RspFileCmdLineChange) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"rule cat_rsp\\n\"\n    \"  command = cat $rspfile > $out\\n\"\n    \"  rspfile = $rspfile\\n\"\n    \"  rspfile_content = $long_command\\n\"\n    \"build out: cat_rsp in\\n\"\n    \"  rspfile = out.rsp\\n\"\n    \"  long_command = Original very long command\\n\"));\n\n  fs_.Create(\"out\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  // 1. Build for the 1st time (-> populate log)\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n  // 2. Build again (no change)\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n  ASSERT_TRUE(builder_.AlreadyUpToDate());\n\n  // 3. Alter the entry in the logfile\n  // (to simulate a change in the command line between 2 builds)\n  BuildLog::LogEntry* log_entry = build_log_.LookupByOutput(\"out\");\n  ASSERT_TRUE(NULL != log_entry);\n  ASSERT_NO_FATAL_FAILURE(AssertHash(\n        \"cat out.rsp > out;rspfile=Original very long command\",\n        log_entry->command_hash));\n  log_entry->command_hash++;  // Change the command hash to something else.\n  // Now expect the target to be rebuilt\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, InterruptCleanup) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule interrupt\\n\"\n\"  command = interrupt\\n\"\n\"rule touch-interrupt\\n\"\n\"  command = touch-interrupt\\n\"\n\"build out1: interrupt in1\\n\"\n\"build out2: touch-interrupt in2\\n\"));\n\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"in2\", \"\");\n\n  // An untouched output of an interrupted command should be retained.\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitInterrupted);\n  EXPECT_EQ(\"interrupted by user\", err);\n  builder_.Cleanup();\n  EXPECT_GT(fs_.Stat(\"out1\", &err), 0);\n  err = \"\";\n\n  // A touched output of an interrupted command should be deleted.\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitInterrupted);\n  EXPECT_EQ(\"interrupted by user\", err);\n  builder_.Cleanup();\n  EXPECT_EQ(0, fs_.Stat(\"out2\", &err));\n}\n\nTEST_F(BuildTest, StatFailureAbortsBuild) {\n  const string kTooLongToStat(400, 'i');\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n(\"build \" + kTooLongToStat + \": cat in\\n\").c_str()));\n  fs_.Create(\"in\", \"\");\n\n  // This simulates a stat failure:\n  fs_.files_[kTooLongToStat].mtime = -1;\n  fs_.files_[kTooLongToStat].stat_error = \"stat failed\";\n\n  string err;\n  EXPECT_FALSE(builder_.AddTarget(kTooLongToStat, &err));\n  EXPECT_EQ(\"stat failed\", err);\n}\n\nTEST_F(BuildTest, PhonyWithNoInputs) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build nonexistent: phony\\n\"\n\"build out1: cat || nonexistent\\n\"\n\"build out2: cat nonexistent\\n\"));\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  // out1 should be up to date even though its input is dirty, because its\n  // order-only dependency has nothing to do.\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n\n  // out2 should still be out of date though, because its input is dirty.\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, DepsGccWithEmptyDepfileErrorsOut) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n\"\n\"  command = cc\\n\"\n\"  deps = gcc\\n\"\n\"build out: cc\\n\"));\n  Dirty(\"out\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_FALSE(builder_.AlreadyUpToDate());\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  ASSERT_EQ(\"subcommand failed\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, StatusFormatElapsed_e) {\n  status_.BuildStarted();\n  // Before any task is done, the elapsed time must be zero.\n  EXPECT_EQ(\"[%/e0.000]\", status_.FormatProgressStatus(\"[%%/e%e]\", 0));\n}\n\nTEST_F(BuildTest, StatusFormatElapsed_w) {\n  status_.BuildStarted();\n  // Before any task is done, the elapsed time must be zero.\n  EXPECT_EQ(\"[%/e00:00]\", status_.FormatProgressStatus(\"[%%/e%w]\", 0));\n}\n\nTEST_F(BuildTest, StatusFormatETA) {\n  status_.BuildStarted();\n  // Before any task is done, the ETA time must be unknown.\n  EXPECT_EQ(\"[%/E?]\", status_.FormatProgressStatus(\"[%%/E%E]\", 0));\n}\n\nTEST_F(BuildTest, StatusFormatTimeProgress) {\n  status_.BuildStarted();\n  // Before any task is done, the percentage of elapsed time must be zero.\n  EXPECT_EQ(\"[%/p  0%]\", status_.FormatProgressStatus(\"[%%/p%p]\", 0));\n}\n\nTEST_F(BuildTest, StatusFormatReplacePlaceholder) {\n  EXPECT_EQ(\"[%/s0/t0/r0/u0/f0]\",\n            status_.FormatProgressStatus(\"[%%/s%s/t%t/r%r/u%u/f%f]\", 0));\n}\n\nTEST_F(BuildTest, FailedDepsParse) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build bad_deps.o: cat in1\\n\"\n\"  deps = gcc\\n\"\n\"  depfile = in1.d\\n\"));\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"bad_deps.o\", &err));\n  ASSERT_EQ(\"\", err);\n\n  // These deps will fail to parse, as they should only have one\n  // path to the left of the colon.\n  fs_.Create(\"in1.d\", \"AAA BBB\");\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  EXPECT_EQ(\"subcommand failed\", err);\n}\n\nstruct BuildWithQueryDepsLogTest : public BuildTest {\n  BuildWithQueryDepsLogTest()\n      : BuildTest(&log_), deps_log_file_(\"ninja_deps\") {}\n\n  ~BuildWithQueryDepsLogTest() {\n    log_.Close();\n  }\n\n  virtual void SetUp() {\n    BuildTest::SetUp();\n\n    temp_dir_.CreateAndEnter(\"BuildWithQueryDepsLogTest\");\n\n    std::string err;\n    ASSERT_TRUE(log_.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n  }\n\n  ScopedTempDir temp_dir_;\n\n  ScopedFilePath deps_log_file_;\n  DepsLog log_;\n};\n\n/// Test a MSVC-style deps log with multiple outputs.\nTEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileMSVC) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cp_multi_msvc\\n\"\n\"    command = echo 'using $in' && for file in $out; do cp $in $$file; done\\n\"\n\"    deps = msvc\\n\"\n\"    msvc_deps_prefix = using \\n\"\n\"build out1 out2: cp_multi_msvc in1\\n\"));\n\n  std::string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"echo 'using in1' && for file in out1 out2; do cp in1 $file; done\", command_runner_.commands_ran_[0]);\n\n  Node* out1_node = state_.LookupNode(\"out1\");\n  DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);\n  EXPECT_EQ(1, out1_deps->node_count);\n  EXPECT_EQ(\"in1\", out1_deps->nodes[0]->path());\n\n  Node* out2_node = state_.LookupNode(\"out2\");\n  DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);\n  EXPECT_EQ(1, out2_deps->node_count);\n  EXPECT_EQ(\"in1\", out2_deps->nodes[0]->path());\n}\n\n/// Test a GCC-style deps log with multiple outputs.\nTEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCOneLine) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cp_multi_gcc\\n\"\n\"    command = echo '$out: $in' > in.d && for file in $out; do cp in1 $$file; done\\n\"\n\"    deps = gcc\\n\"\n\"    depfile = in.d\\n\"\n\"build out1 out2: cp_multi_gcc in1 in2\\n\"));\n\n  std::string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  fs_.Create(\"in.d\", \"out1 out2: in1 in2\");\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"echo 'out1 out2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done\", command_runner_.commands_ran_[0]);\n\n  Node* out1_node = state_.LookupNode(\"out1\");\n  DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);\n  EXPECT_EQ(2, out1_deps->node_count);\n  EXPECT_EQ(\"in1\", out1_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out1_deps->nodes[1]->path());\n\n  Node* out2_node = state_.LookupNode(\"out2\");\n  DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);\n  EXPECT_EQ(2, out2_deps->node_count);\n  EXPECT_EQ(\"in1\", out2_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out2_deps->nodes[1]->path());\n}\n\n/// Test a GCC-style deps log with multiple outputs using a line per input.\nTEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCMultiLineInput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cp_multi_gcc\\n\"\n\"    command = echo '$out: in1\\\\n$out: in2' > in.d && for file in $out; do cp in1 $$file; done\\n\"\n\"    deps = gcc\\n\"\n\"    depfile = in.d\\n\"\n\"build out1 out2: cp_multi_gcc in1 in2\\n\"));\n\n  std::string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  fs_.Create(\"in.d\", \"out1 out2: in1\\nout1 out2: in2\");\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"echo 'out1 out2: in1\\\\nout1 out2: in2' > in.d && for file in out1 out2; do cp in1 $file; done\", command_runner_.commands_ran_[0]);\n\n  Node* out1_node = state_.LookupNode(\"out1\");\n  DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);\n  EXPECT_EQ(2, out1_deps->node_count);\n  EXPECT_EQ(\"in1\", out1_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out1_deps->nodes[1]->path());\n\n  Node* out2_node = state_.LookupNode(\"out2\");\n  DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);\n  EXPECT_EQ(2, out2_deps->node_count);\n  EXPECT_EQ(\"in1\", out2_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out2_deps->nodes[1]->path());\n}\n\n/// Test a GCC-style deps log with multiple outputs using a line per output.\nTEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCMultiLineOutput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cp_multi_gcc\\n\"\n\"    command = echo 'out1: $in\\\\nout2: $in' > in.d && for file in $out; do cp in1 $$file; done\\n\"\n\"    deps = gcc\\n\"\n\"    depfile = in.d\\n\"\n\"build out1 out2: cp_multi_gcc in1 in2\\n\"));\n\n  std::string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  fs_.Create(\"in.d\", \"out1: in1 in2\\nout2: in1 in2\");\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"echo 'out1: in1 in2\\\\nout2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done\", command_runner_.commands_ran_[0]);\n\n  Node* out1_node = state_.LookupNode(\"out1\");\n  DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);\n  EXPECT_EQ(2, out1_deps->node_count);\n  EXPECT_EQ(\"in1\", out1_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out1_deps->nodes[1]->path());\n\n  Node* out2_node = state_.LookupNode(\"out2\");\n  DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);\n  EXPECT_EQ(2, out2_deps->node_count);\n  EXPECT_EQ(\"in1\", out2_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out2_deps->nodes[1]->path());\n}\n\n/// Test a GCC-style deps log with multiple outputs mentioning only the main output.\nTEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCOnlyMainOutput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cp_multi_gcc\\n\"\n\"    command = echo 'out1: $in' > in.d && for file in $out; do cp in1 $$file; done\\n\"\n\"    deps = gcc\\n\"\n\"    depfile = in.d\\n\"\n\"build out1 out2: cp_multi_gcc in1 in2\\n\"));\n\n  std::string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  fs_.Create(\"in.d\", \"out1: in1 in2\");\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"echo 'out1: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done\", command_runner_.commands_ran_[0]);\n\n  Node* out1_node = state_.LookupNode(\"out1\");\n  DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);\n  EXPECT_EQ(2, out1_deps->node_count);\n  EXPECT_EQ(\"in1\", out1_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out1_deps->nodes[1]->path());\n\n  Node* out2_node = state_.LookupNode(\"out2\");\n  DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);\n  EXPECT_EQ(2, out2_deps->node_count);\n  EXPECT_EQ(\"in1\", out2_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out2_deps->nodes[1]->path());\n}\n\n/// Test a GCC-style deps log with multiple outputs mentioning only the secondary output.\nTEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCOnlySecondaryOutput) {\n  // Note: This ends up short-circuiting the node creation due to the primary\n  // output not being present, but it should still work.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cp_multi_gcc\\n\"\n\"    command = echo 'out2: $in' > in.d && for file in $out; do cp in1 $$file; done\\n\"\n\"    deps = gcc\\n\"\n\"    depfile = in.d\\n\"\n\"build out1 out2: cp_multi_gcc in1 in2\\n\"));\n\n  std::string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  ASSERT_EQ(\"\", err);\n  fs_.Create(\"in.d\", \"out2: in1 in2\");\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"echo 'out2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done\", command_runner_.commands_ran_[0]);\n\n  Node* out1_node = state_.LookupNode(\"out1\");\n  DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);\n  EXPECT_EQ(2, out1_deps->node_count);\n  EXPECT_EQ(\"in1\", out1_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out1_deps->nodes[1]->path());\n\n  Node* out2_node = state_.LookupNode(\"out2\");\n  DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);\n  EXPECT_EQ(2, out2_deps->node_count);\n  EXPECT_EQ(\"in1\", out2_deps->nodes[0]->path());\n  EXPECT_EQ(\"in2\", out2_deps->nodes[1]->path());\n}\n\n/// Tests of builds involving deps logs necessarily must span\n/// multiple builds.  We reuse methods on BuildTest but not the\n/// builder_ it sets up, because we want pristine objects for\n/// each build.\nstruct BuildWithDepsLogTest : public BuildTest {\n  BuildWithDepsLogTest()\n      : build_log_file_(\"build_log\"), deps_log_file_(\"ninja_deps\") {}\n\n  virtual void SetUp() {\n    BuildTest::SetUp();\n\n    temp_dir_.CreateAndEnter(\"BuildWithDepsLogTest\");\n  }\n\n  virtual void TearDown() {\n    temp_dir_.Cleanup();\n  }\n\n  ScopedTempDir temp_dir_;\n  ScopedFilePath build_log_file_;\n  ScopedFilePath deps_log_file_;\n\n  /// Shadow parent class builder_ so we don't accidentally use it.\n  void* builder_;\n};\n\n/// Run a straightforward build where the deps log is used.\nTEST_F(BuildWithDepsLogTest, Straightforward) {\n  string err;\n  // Note: in1 was created by the superclass SetUp().\n  const char* manifest =\n      \"build out: cat in1\\n\"\n      \"  deps = gcc\\n\"\n      \"  depfile = in1.d\\n\";\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    // Run the build once, everything should be ok.\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    fs_.Create(\"in1.d\", \"out: in2\");\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    // The deps file should have been removed.\n    EXPECT_EQ(0, fs_.Stat(\"in1.d\", &err));\n    // Recreate it for the next step.\n    fs_.Create(\"in1.d\", \"out: in2\");\n    deps_log.Close();\n  }\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    // Touch the file only mentioned in the deps.\n    fs_.Tick();\n    fs_.Create(\"in2\", \"\");\n\n    // Run the build again.\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    // We should have rebuilt the output due to in2 being\n    // out of date.\n    EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n  }\n}\n\n/// Verify that obsolete dependency info causes a rebuild.\n/// 1) Run a successful build where everything has time t, record deps.\n/// 2) Move input/output to time t+1 -- despite files in alignment,\n///    should still need to rebuild due to deps at older time.\nTEST_F(BuildWithDepsLogTest, ObsoleteDeps) {\n  string err;\n  // Note: in1 was created by the superclass SetUp().\n  const char* manifest =\n      \"build out: cat in1\\n\"\n      \"  deps = gcc\\n\"\n      \"  depfile = in1.d\\n\";\n  {\n    // Run an ordinary build that gathers dependencies.\n    fs_.Create(\"in1\", \"\");\n    fs_.Create(\"in1.d\", \"out: \");\n\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    // Run the build once, everything should be ok.\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    deps_log.Close();\n  }\n\n  // Push all files one tick forward so that only the deps are out\n  // of date.\n  fs_.Tick();\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out\", \"\");\n\n  // The deps file should have been removed, so no need to timestamp it.\n  EXPECT_EQ(0, fs_.Stat(\"in1.d\", &err));\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n\n    // Recreate the deps file here because the build expects them to exist.\n    fs_.Create(\"in1.d\", \"out: \");\n\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    // We should have rebuilt the output due to the deps being\n    // out of date.\n    EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n  }\n}\n\nTEST_F(BuildWithDepsLogTest, DepsIgnoredInDryRun) {\n  const char* manifest =\n      \"build out: cat in1\\n\"\n      \"  deps = gcc\\n\"\n      \"  depfile = in1.d\\n\";\n\n  fs_.Create(\"out\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in1\", \"\");\n\n  State state;\n  ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n  // The deps log is NULL in dry runs.\n  config_.dry_run = true;\n  Builder builder(&state, config_, NULL, NULL, &fs_, &status_, 0);\n  builder.command_runner_.reset(&command_runner_);\n  SafeRelease protect(&builder);\n  command_runner_.commands_ran_.clear();\n\n  string err;\n  EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder.Build(&err), ExitSuccess);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildWithDepsLogTest, TestInputMtimeRaceCondition) {\n  string err;\n  const char* manifest =\n      \"rule long-cc\\n\"\n      \"  command = long-cc\\n\"\n      \"build out: long-cc in1\\n\"\n      \"  test_dependency = in1\\n\";\n\n  State state;\n  ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n  BuildLog build_log;\n  ASSERT_TRUE(build_log.Load(build_log_file_.path(), &err));\n  ASSERT_TRUE(build_log.OpenForWrite(build_log_file_.path(), *this, &err));\n\n  DepsLog deps_log;\n  ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n  ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n\n  BuildLog::LogEntry* log_entry = NULL;\n  {\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    // Run the build, out gets built, dep file is created\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n    // See that an entry in the logfile is created. the input_mtime is 1 since that was\n    // the mtime of in1 when the command was started\n    log_entry = build_log.LookupByOutput(\"out\");\n    ASSERT_TRUE(NULL != log_entry);\n    ASSERT_EQ(1u, log_entry->mtime);\n  }\n\n  {\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    // Trigger the build again - \"out\" should rebuild despite having a newer mtime than\n    // \"in1\", since \"in1\" was touched during the build of out (simulated by changing its\n    // mtime in the the test builder's WaitForCommand() which runs before FinishCommand()\n    command_runner_.commands_ran_.clear();\n    state.Reset();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n    // Check that the logfile entry is still correct\n    log_entry = build_log.LookupByOutput(\"out\");\n    ASSERT_TRUE(NULL != log_entry);\n    ASSERT_TRUE(fs_.files_[\"in1\"].mtime < log_entry->mtime);\n  }\n\n  {\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    // And a subsequent run should not have any work to do\n    command_runner_.commands_ran_.clear();\n    state.Reset();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_TRUE(builder.AlreadyUpToDate());\n  }\n}\n\nTEST_F(BuildWithDepsLogTest, TestInputMtimeRaceConditionWithDepFile) {\n  string err;\n  const char* manifest =\n      \"rule long-cc\\n\"\n      \"  command = long-cc\\n\"\n      \"build out: long-cc\\n\"\n      \"  deps = gcc\\n\"\n      \"  depfile = out.d\\n\"\n      \"  test_dependency = header.h\\n\";\n\n  fs_.Create(\"header.h\", \"\");\n\n  State state;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n  BuildLog build_log;\n  ASSERT_TRUE(build_log.Load(build_log_file_.path(), &err));\n  ASSERT_TRUE(build_log.OpenForWrite(build_log_file_.path(), *this, &err));\n\n  DepsLog deps_log;\n  ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n  ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n\n  {\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n\n    // Run the build, out gets built, dep file is created\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n\n    // See that an entry in the logfile is created. the mtime is 1 due to the command\n    // starting when the file system's mtime was 1.\n    BuildLog::LogEntry* log_entry = build_log.LookupByOutput(\"out\");\n    ASSERT_TRUE(NULL != log_entry);\n    ASSERT_EQ(1u, log_entry->mtime);\n  }\n\n  {\n    // Trigger the build again - \"out\" will rebuild since its newest input mtime (header.h)\n    // is newer than the recorded mtime of out in the build log\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    state.Reset();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  }\n\n  {\n    // Trigger the build again - \"out\" won't rebuild since the file wasn't updated during\n    // the previous build\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    state.Reset();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    ASSERT_TRUE(builder.AlreadyUpToDate());\n  }\n\n  // touch the header to trigger a rebuild\n  fs_.Create(\"header.h\", \"\");\n  ASSERT_EQ(fs_.now_, 7);\n\n  {\n    // Rebuild. This time, long-cc will cause header.h to be updated while the build is\n    // in progress\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    state.Reset();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  }\n\n  {\n    // Rebuild. Because header.h is now in the deplog for out, it should be detectable as\n    // a change-while-in-progress and should cause a rebuild of out.\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    state.Reset();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  }\n\n  {\n    // This time, the header.h file was not updated during the build, so the target should\n    // not be considered dirty.\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n\n    state.Reset();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_TRUE(builder.AlreadyUpToDate());\n  }\n}\n\n/// Check that a restat rule generating a header cancels compilations correctly.\nTEST_F(BuildTest, RestatDepfileDependency) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule true\\n\"\n\"  command = true\\n\"  // Would be \"write if out-of-date\" in reality.\n\"  restat = 1\\n\"\n\"build header.h: true header.in\\n\"\n\"build out: cat in1\\n\"\n\"  depfile = in1.d\\n\"));\n\n  fs_.Create(\"header.h\", \"\");\n  fs_.Create(\"in1.d\", \"out: header.h\");\n  fs_.Tick();\n  fs_.Create(\"header.in\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n}\n\n/// Check that a restat rule generating a header cancels compilations correctly,\n/// depslog case.\nTEST_F(BuildWithDepsLogTest, RestatDepfileDependencyDepsLog) {\n  string err;\n  // Note: in1 was created by the superclass SetUp().\n  const char* manifest =\n      \"rule true\\n\"\n      \"  command = true\\n\"  // Would be \"write if out-of-date\" in reality.\n      \"  restat = 1\\n\"\n      \"build header.h: true header.in\\n\"\n      \"build out: cat in1\\n\"\n      \"  deps = gcc\\n\"\n      \"  depfile = in1.d\\n\";\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    // Run the build once, everything should be ok.\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    fs_.Create(\"in1.d\", \"out: header.h\");\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    deps_log.Close();\n  }\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    // Touch the input of the restat rule.\n    fs_.Tick();\n    fs_.Create(\"header.in\", \"\");\n\n    // Run the build again.\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    command_runner_.commands_ran_.clear();\n    EXPECT_TRUE(builder.AddTarget(\"out\", &err));\n    ASSERT_EQ(\"\", err);\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    // Rule \"true\" should have run again, but the build of \"out\" should have\n    // been cancelled due to restat propagating through the depfile header.\n    EXPECT_EQ(1u, command_runner_.commands_ran_.size());\n  }\n}\n\nTEST_F(BuildWithDepsLogTest, DepFileOKDepsLog) {\n  string err;\n  const char* manifest =\n      \"rule cc\\n  command = cc $in\\n  depfile = $out.d\\n  deps = gcc\\n\"\n      \"build fo$ o.o: cc foo.c\\n\";\n\n  fs_.Create(\"foo.c\", \"\");\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    // Run the build once, everything should be ok.\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"fo o.o\", &err));\n    ASSERT_EQ(\"\", err);\n    fs_.Create(\"fo o.o.d\", \"fo\\\\ o.o: blah.h bar.h\\n\");\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    deps_log.Close();\n  }\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n\n    Edge* edge = state.edges_.back();\n\n    state.GetNode(\"bar.h\", 0)->MarkDirty();  // Mark bar.h as missing.\n    EXPECT_TRUE(builder.AddTarget(\"fo o.o\", &err));\n    ASSERT_EQ(\"\", err);\n\n    // Expect one new edge generating fo o.o, loading the depfile should\n    // not generate new edges.\n    ASSERT_EQ(1u, state.edges_.size());\n    // Expect our edge to now have three inputs: foo.c and two headers.\n    ASSERT_EQ(3u, edge->inputs_.size());\n\n    // Expect the command line we generate to only use the original input.\n    ASSERT_EQ(\"cc foo.c\", edge->EvaluateCommand());\n\n    deps_log.Close();\n  }\n}\n\nTEST_F(BuildWithDepsLogTest, DiscoveredDepDuringBuildChanged) {\n  string err;\n  const char* manifest =\n    \"rule touch-out-implicit-dep\\n\"\n    \"  command = touch $out ; sleep 1 ; touch $test_dependency\\n\"\n    \"rule generate-depfile\\n\"\n    \"  command = touch $out ; echo \\\"$out: $test_dependency\\\" > $depfile\\n\"\n    \"build out1: touch-out-implicit-dep in1\\n\"\n    \"  test_dependency = inimp\\n\"\n    \"build out2: generate-depfile in1 || out1\\n\"\n    \"  test_dependency = inimp\\n\"\n    \"  depfile = out2.d\\n\"\n    \"  deps = gcc\\n\";\n\n  fs_.Create(\"in1\", \"\");\n  fs_.Tick();\n\n  BuildLog build_log;\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"out2\", &err));\n    EXPECT_FALSE(builder.AlreadyUpToDate());\n\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_TRUE(builder.AlreadyUpToDate());\n\n    deps_log.Close();\n  }\n\n  fs_.Tick();\n  fs_.Create(\"in1\", \"\");\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"out2\", &err));\n    EXPECT_FALSE(builder.AlreadyUpToDate());\n\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_TRUE(builder.AlreadyUpToDate());\n\n    deps_log.Close();\n  }\n\n  fs_.Tick();\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, &build_log, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"out2\", &err));\n    EXPECT_TRUE(builder.AlreadyUpToDate());\n\n    deps_log.Close();\n  }\n}\n\n#ifdef _WIN32\nTEST_F(BuildWithDepsLogTest, DepFileDepsLogCanonicalize) {\n  string err;\n  const char* manifest =\n      \"rule cc\\n  command = cc $in\\n  depfile = $out.d\\n  deps = gcc\\n\"\n      \"build a/b\\\\c\\\\d/e/fo$ o.o: cc x\\\\y/z\\\\foo.c\\n\";\n\n  fs_.Create(\"x/y/z/foo.c\", \"\");\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    // Run the build once, everything should be ok.\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n    EXPECT_TRUE(builder.AddTarget(\"a/b/c/d/e/fo o.o\", &err));\n    ASSERT_EQ(\"\", err);\n    // Note, different slashes from manifest.\n    fs_.Create(\"a/b\\\\c\\\\d/e/fo o.o.d\",\n               \"a\\\\b\\\\c\\\\d\\\\e\\\\fo\\\\ o.o: blah.h bar.h\\n\");\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    deps_log.Close();\n  }\n\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n\n    state.GetNode(\"bar.h\", 0)->MarkDirty();  // Mark bar.h as missing.\n    EXPECT_TRUE(builder.AddTarget(\"a/b/c/d/e/fo o.o\", &err));\n    ASSERT_EQ(\"\", err);\n\n    // Expect one new edge generating fo o.o.\n    ASSERT_EQ(1u, state.edges_.size());\n    // Expect our edge to now have three inputs: foo.c and two headers.\n    Edge* edge = state.edges_.back();\n    ASSERT_EQ(3u, edge->inputs_.size());\n\n    // Expect the command line we generate to only use the original input.\n    // Note, slashes from manifest, not .d.\n    ASSERT_EQ(\"cc x\\\\y/z\\\\foo.c\", edge->EvaluateCommand());\n\n    deps_log.Close();\n  }\n}\n#endif\n\n/// Check that a restat rule doesn't clear an edge if the depfile is missing.\n/// Follows from: https://github.com/ninja-build/ninja/issues/603\nTEST_F(BuildTest, RestatMissingDepfile) {\nconst char* manifest =\n\"rule true\\n\"\n\"  command = true\\n\"  // Would be \"write if out-of-date\" in reality.\n\"  restat = 1\\n\"\n\"build header.h: true header.in\\n\"\n\"build out: cat header.h\\n\"\n\"  depfile = out.d\\n\";\n\n  fs_.Create(\"header.h\", \"\");\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n  fs_.Create(\"header.in\", \"\");\n\n  // Normally, only 'header.h' would be rebuilt, as\n  // its rule doesn't touch the output and has 'restat=1' set.\n  // But we are also missing the depfile for 'out',\n  // which should force its command to run anyway!\n  RebuildTarget(\"out\", manifest);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n}\n\n/// Check that a restat rule doesn't clear an edge if the deps are missing.\n/// https://github.com/ninja-build/ninja/issues/603\nTEST_F(BuildWithDepsLogTest, RestatMissingDepfileDepslog) {\n  string err;\n  const char* manifest =\n\"rule true\\n\"\n\"  command = true\\n\"  // Would be \"write if out-of-date\" in reality.\n\"  restat = 1\\n\"\n\"build header.h: true header.in\\n\"\n\"build out: cat header.h\\n\"\n\"  deps = gcc\\n\"\n\"  depfile = out.d\\n\";\n\n  // Build once to populate ninja deps logs from out.d\n  fs_.Create(\"header.in\", \"\");\n  fs_.Create(\"out.d\", \"out: header.h\");\n  fs_.Create(\"header.h\", \"\");\n\n  RebuildTarget(\"out\", manifest, build_log_file_.c_str(),\n                deps_log_file_.c_str());\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // Sanity: this rebuild should be NOOP\n  RebuildTarget(\"out\", manifest, build_log_file_.c_str(),\n                deps_log_file_.c_str());\n  ASSERT_EQ(0u, command_runner_.commands_ran_.size());\n\n  // Touch 'header.in', blank dependencies log (create a different one).\n  // Building header.h triggers 'restat' outputs cleanup.\n  // Validate that out is rebuilt nevertheless, as deps are missing.\n  fs_.Tick();\n  fs_.Create(\"header.in\", \"\");\n\n  ScopedFilePath deps2_file_(\"ninja_deps2\");\n\n  // (switch to a new blank deps_log \"ninja_deps2\")\n  RebuildTarget(\"out\", manifest, build_log_file_.c_str(), deps2_file_.c_str());\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // Sanity: this build should be NOOP\n  RebuildTarget(\"out\", manifest, build_log_file_.c_str(), deps2_file_.c_str());\n  ASSERT_EQ(0u, command_runner_.commands_ran_.size());\n\n  // Check that invalidating deps by target timestamp also works here\n  // Repeat the test but touch target instead of blanking the log.\n  fs_.Tick();\n  fs_.Create(\"header.in\", \"\");\n  fs_.Create(\"out\", \"\");\n  RebuildTarget(\"out\", manifest, build_log_file_.c_str(), deps2_file_.c_str());\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // And this build should be NOOP again\n  RebuildTarget(\"out\", manifest, build_log_file_.c_str(), deps2_file_.c_str());\n  ASSERT_EQ(0u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, WrongOutputInDepfileCausesRebuild) {\n  string err;\n  const char* manifest =\n\"rule cc\\n\"\n\"  command = cc $in\\n\"\n\"  depfile = $out.d\\n\"\n\"build foo.o: cc foo.c\\n\";\n\n  fs_.Create(\"foo.c\", \"\");\n  fs_.Create(\"foo.o\", \"\");\n  fs_.Create(\"header.h\", \"\");\n  fs_.Create(\"foo.o.d\", \"bar.o.d: header.h\\n\");\n\n  ScopedFilePath build_log(\"build_log\");\n  ScopedFilePath deps_file(\"ninja_deps\");\n\n  RebuildTarget(\"foo.o\", manifest, build_log.c_str(), deps_file.c_str());\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, Console) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule console\\n\"\n\"  command = console\\n\"\n\"  pool = console\\n\"\n\"build cons: console in.txt\\n\"));\n\n  fs_.Create(\"in.txt\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"cons\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n}\n\nTEST_F(BuildTest, DyndepMissingAndNoRule) {\n  // Verify that we can diagnose when a dyndep file is missing and\n  // has no rule to build it.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n\n  string err;\n  EXPECT_FALSE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"loading 'dd': No such file or directory\", err);\n}\n\nTEST_F(BuildTest, DyndepReadyImplicitConnection) {\n  // Verify that a dyndep file can be loaded immediately to discover\n  // that one edge has an implicit output that is also an implicit\n  // input of another edge.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"build tmp: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out | out.imp: dyndep | tmp.imp\\n\"\n\"build tmp | tmp.imp: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"touch tmp tmp.imp\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch out out.imp\", command_runner_.commands_ran_[1]);\n}\n\nTEST_F(BuildTest, DyndepReadySyntaxError) {\n  // Verify that a dyndep file can be loaded immediately to discover\n  // and reject a syntax error in it.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n  fs_.Create(\"dd\",\n\"build out: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_FALSE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"dd:1: expected 'ninja_dyndep_version = ...'\\n\", err);\n}\n\nTEST_F(BuildTest, DyndepReadyCircular) {\n  // Verify that a dyndep file can be loaded immediately to discover\n  // and reject a circular dependency.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r in || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build in: r circ\\n\"\n  ));\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out | circ: dyndep\\n\"\n  );\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_FALSE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"dependency cycle: circ -> in -> circ\", err);\n}\n\nTEST_F(BuildTest, DyndepBuild) {\n  // Verify that a dyndep file can be built and loaded to discover nothing.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  size_t files_created = fs_.files_created_.size();\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch out\", command_runner_.commands_ran_[1]);\n  ASSERT_EQ(2u, fs_.files_read_.size());\n  EXPECT_EQ(\"dd-in\", fs_.files_read_[0]);\n  EXPECT_EQ(\"dd\", fs_.files_read_[1]);\n  ASSERT_EQ(3u + files_created, fs_.files_created_.size());\n  EXPECT_EQ(1u, fs_.files_created_.count(\"dd\"));\n  EXPECT_EQ(1u, fs_.files_created_.count(\"out\"));\n  EXPECT_EQ(1u, fs_.files_created_.count(\".ninja_lock\"));\n}\n\nTEST_F(BuildTest, DyndepBuildSyntaxError) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // and reject a syntax error in it.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n  fs_.Create(\"dd-in\",\n\"build out: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  EXPECT_EQ(\"dd:1: expected 'ninja_dyndep_version = ...'\\n\", err);\n}\n\nTEST_F(BuildTest, DyndepBuildUnrelatedOutput) {\n  // Verify that a dyndep file can have dependents that do not specify\n  // it as their dyndep binding.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build unrelated: touch || dd\\n\"\n\"build out: touch unrelated || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch unrelated\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch out\", command_runner_.commands_ran_[2]);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNewOutput) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // a new output of an edge.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out: touch in || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out | out.imp: dyndep\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(2u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch out out.imp\", command_runner_.commands_ran_[1]);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNewOutputWithMultipleRules1) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // a new output of an edge that is already the output of another edge.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out1 | out-twice.imp: touch in\\n\"\n\"build out2: touch in || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out2 | out-twice.imp: dyndep\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  EXPECT_EQ(\"multiple rules generate out-twice.imp\", err);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNewOutputWithMultipleRules2) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // a new output of an edge that is already the output of another\n  // edge also discovered by dyndep.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd1: cp dd1-in\\n\"\n\"build out1: touch || dd1\\n\"\n\"  dyndep = dd1\\n\"\n\"build dd2: cp dd2-in || dd1\\n\" // make order predictable for test\n\"build out2: touch || dd2\\n\"\n\"  dyndep = dd2\\n\"\n));\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Create(\"dd1-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out1 | out-twice.imp: dyndep\\n\"\n);\n  fs_.Create(\"dd2-in\", \"\");\n  fs_.Create(\"dd2\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out2 | out-twice.imp: dyndep\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  EXPECT_EQ(\"multiple rules generate out-twice.imp\", err);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNewInput) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // a new input to an edge.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build in: touch\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | in\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch in\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch out\", command_runner_.commands_ran_[2]);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNewInputWithValidation) {\n  // Verify that a dyndep file cannot contain the |@ validation\n  // syntax.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep |@ validation\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n\n  string err_first_line = err.substr(0, err.find(\"\\n\"));\n  EXPECT_EQ(\"dd:2: expected newline, got '|@'\", err_first_line);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNewInputWithTransitiveValidation) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // a new input to an edge that has a validation edge.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build in: touch |@ validation\\n\"\n\"build validation: touch in out\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | in\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(4u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch in\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch out\", command_runner_.commands_ran_[2]);\n  EXPECT_EQ(\"touch validation\", command_runner_.commands_ran_[3]);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverImplicitConnection) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // that one edge has an implicit output that is also an implicit\n  // input of another edge.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build tmp: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out | out.imp: dyndep | tmp.imp\\n\"\n\"build tmp | tmp.imp: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch tmp tmp.imp\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch out out.imp\", command_runner_.commands_ran_[2]);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverOutputAndDepfileInput) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // that one edge has an implicit output that is also reported by\n  // a depfile as an input of another edge.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build tmp: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out: cp tmp\\n\"\n\"  depfile = out.d\\n\"\n));\n  fs_.Create(\"out.d\", \"out: tmp.imp\\n\");\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build tmp | tmp.imp: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  // Loading the depfile did not give tmp.imp a phony input edge.\n  ASSERT_FALSE(GetNode(\"tmp.imp\")->in_edge());\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  // Loading the dyndep file gave tmp.imp a real input edge.\n  ASSERT_FALSE(GetNode(\"tmp.imp\")->in_edge()->is_phony());\n\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch tmp tmp.imp\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"cp tmp out\", command_runner_.commands_ran_[2]);\n  EXPECT_EQ(1u, fs_.files_created_.count(\"tmp.imp\"));\n  EXPECT_TRUE(builder_.AlreadyUpToDate());\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNowWantEdge) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // that an edge is actually wanted due to a missing implicit output.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build tmp: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out: touch tmp || dd\\n\"\n\"  dyndep = dd\\n\"\n));\n  fs_.Create(\"tmp\", \"\");\n  fs_.Create(\"out\", \"\");\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"build tmp | tmp.imp: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(std::vector<std::string>(\n                { \"cp dd-in dd\", \"touch tmp tmp.imp\", \"touch out out.imp\" }),\n            command_runner_.commands_ran_);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverNowWantEdgeAndDependent) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // that an edge and a dependent are actually wanted.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build tmp: touch || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out: touch tmp\\n\"\n));\n  fs_.Create(\"tmp\", \"\");\n  fs_.Create(\"out\", \"\");\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build tmp | tmp.imp: dyndep\\n\"\n);\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch tmp tmp.imp\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch out out.imp\", command_runner_.commands_ran_[2]);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverCircular) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // and reject a circular dependency.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out: r in || dd\\n\"\n\"  depfile = out.d\\n\"\n\"  dyndep = dd\\n\"\n\"build in: r || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"out.d\", \"out: inimp\\n\");\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out | circ: dyndep\\n\"\n\"build in: dyndep | circ\\n\"\n  );\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitFailure);\n  // Depending on how the pointers in Plan::ready_ work out, we could have\n  // discovered the cycle from either starting point.\n  EXPECT_TRUE(err == \"dependency cycle: circ -> in -> circ\" ||\n              err == \"dependency cycle: in -> circ -> in\");\n}\n\nTEST_F(BuildWithLogTest, DyndepBuildDiscoverRestat) {\n  // Verify that a dyndep file can be built and loaded to discover\n  // that an edge has a restat binding.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule true\\n\"\n\"  command = true\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd: cp dd-in\\n\"\n\"build out1: true in || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out2: cat out1\\n\"));\n\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Create(\"dd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out1: dyndep\\n\"\n\"  restat = 1\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  // Do a pre-build so that there's commands in the log for the outputs,\n  // otherwise, the lack of an entry in the build log will cause \"out2\" to\n  // rebuild regardless of restat.\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd-in dd\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"true\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"cat out1 > out2\", command_runner_.commands_ran_[2]);\n\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  // We touched \"in\", so we should build \"out1\".  But because \"true\" does not\n  // touch \"out1\", we should cancel the build of \"out2\".\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"true\", command_runner_.commands_ran_[0]);\n}\n\nTEST_F(BuildTest, DyndepBuildDiscoverScheduledEdge) {\n  // Verify that a dyndep file can be built and loaded to discover a\n  // new input that itself is an output from an edge that has already\n  // been scheduled but not finished.  We should not re-schedule it.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build out1 | out1.imp: touch\\n\"\n\"build zdd: cp zdd-in\\n\"\n\"  verify_active_edge = out1\\n\" // verify out1 is active when zdd is finished\n\"build out2: cp out1 || zdd\\n\"\n\"  dyndep = zdd\\n\"\n));\n  fs_.Create(\"zdd-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out2: dyndep | out1.imp\\n\"\n);\n\n  // Enable concurrent builds so that we can load the dyndep file\n  // while another edge is still active.\n  command_runner_.max_active_edges_ = 2;\n\n  // During the build \"out1\" and \"zdd\" should be built concurrently.\n  // The fake command runner will finish these in reverse order\n  // of the names of the first outputs, so \"zdd\" will finish first\n  // and we will load the dyndep file while the edge for \"out1\" is\n  // still active.  This will add a new dependency on \"out1.imp\",\n  // also produced by the active edge.  The builder should not\n  // re-schedule the already-active edge.\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out1\", &err));\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  // Depending on how the pointers in Plan::ready_ work out, the first\n  // two commands may have run in either order.\n  EXPECT_TRUE((command_runner_.commands_ran_[0] == \"touch out1 out1.imp\" &&\n               command_runner_.commands_ran_[1] == \"cp zdd-in zdd\") ||\n              (command_runner_.commands_ran_[1] == \"touch out1 out1.imp\" &&\n               command_runner_.commands_ran_[0] == \"cp zdd-in zdd\"));\n  EXPECT_EQ(\"cp out1 out2\", command_runner_.commands_ran_[2]);\n}\n\nTEST_F(BuildTest, DyndepTwoLevelDirect) {\n  // Verify that a clean dyndep file can depend on a dirty dyndep file\n  // and be loaded properly after the dirty one is built and loaded.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd1: cp dd1-in\\n\"\n\"build out1 | out1.imp: touch || dd1\\n\"\n\"  dyndep = dd1\\n\"\n\"build dd2: cp dd2-in || dd1\\n\" // direct order-only dep on dd1\n\"build out2: touch || dd2\\n\"\n\"  dyndep = dd2\\n\"\n));\n  fs_.Create(\"out1.imp\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Create(\"out2.imp\", \"\");\n  fs_.Create(\"dd1-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out1: dyndep\\n\"\n);\n  fs_.Create(\"dd2-in\", \"\");\n  fs_.Create(\"dd2\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out2 | out2.imp: dyndep | out1.imp\\n\"\n);\n\n  // During the build dd1 should be built and loaded.  The RecomputeDirty\n  // called as a result of loading dd1 should not cause dd2 to be loaded\n  // because the builder will never get a chance to update the build plan\n  // to account for dd2.  Instead dd2 should only be later loaded once the\n  // builder recognizes that it is now ready (as its order-only dependency\n  // on dd1 has been satisfied).  This test case verifies that each dyndep\n  // file is loaded to update the build graph independently.\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd1-in dd1\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch out1 out1.imp\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch out2 out2.imp\", command_runner_.commands_ran_[2]);\n}\n\nTEST_F(BuildTest, DyndepTwoLevelIndirect) {\n  // Verify that dyndep files can add to an edge new implicit inputs that\n  // correspond to implicit outputs added to other edges by other dyndep\n  // files on which they (order-only) depend.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out $out.imp\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd1: cp dd1-in\\n\"\n\"build out1: touch || dd1\\n\"\n\"  dyndep = dd1\\n\"\n\"build dd2: cp dd2-in || out1\\n\" // indirect order-only dep on dd1\n\"build out2: touch || dd2\\n\"\n\"  dyndep = dd2\\n\"\n));\n  fs_.Create(\"out1.imp\", \"\");\n  fs_.Create(\"out2\", \"\");\n  fs_.Create(\"out2.imp\", \"\");\n  fs_.Create(\"dd1-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out1 | out1.imp: dyndep\\n\"\n);\n  fs_.Create(\"dd2-in\", \"\");\n  fs_.Create(\"dd2\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out2 | out2.imp: dyndep | out1.imp\\n\"\n);\n\n  // During the build dd1 should be built and loaded.  Then dd2 should\n  // be built and loaded.  Loading dd2 should cause the builder to\n  // recognize that out2 needs to be built even though it was originally\n  // clean without dyndep info.\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out2\", &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd1-in dd1\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch out1 out1.imp\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch out2 out2.imp\", command_runner_.commands_ran_[2]);\n}\n\nTEST_F(BuildTest, DyndepTwoLevelDiscoveredReady) {\n  // Verify that a dyndep file can discover a new input whose\n  // edge also has a dyndep file that is ready to load immediately.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd0: cp dd0-in\\n\"\n\"build dd1: cp dd1-in\\n\"\n\"build in: touch\\n\"\n\"build tmp: touch || dd0\\n\"\n\"  dyndep = dd0\\n\"\n\"build out: touch || dd1\\n\"\n\"  dyndep = dd1\\n\"\n  ));\n  fs_.Create(\"dd1-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | tmp\\n\"\n);\n  fs_.Create(\"dd0-in\", \"\");\n  fs_.Create(\"dd0\",\n\"ninja_dyndep_version = 1\\n\"\n\"build tmp: dyndep | in\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(4u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd1-in dd1\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"touch in\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch tmp\", command_runner_.commands_ran_[2]);\n  EXPECT_EQ(\"touch out\", command_runner_.commands_ran_[3]);\n}\n\nTEST_F(BuildTest, DyndepTwoLevelDiscoveredDirty) {\n  // Verify that a dyndep file can discover a new input whose\n  // edge also has a dyndep file that needs to be built.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"rule cp\\n\"\n\"  command = cp $in $out\\n\"\n\"build dd0: cp dd0-in\\n\"\n\"build dd1: cp dd1-in\\n\"\n\"build in: touch\\n\"\n\"build tmp: touch || dd0\\n\"\n\"  dyndep = dd0\\n\"\n\"build out: touch || dd1\\n\"\n\"  dyndep = dd1\\n\"\n  ));\n  fs_.Create(\"dd1-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | tmp\\n\"\n);\n  fs_.Create(\"dd0-in\",\n\"ninja_dyndep_version = 1\\n\"\n\"build tmp: dyndep | in\\n\"\n);\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(5u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cp dd1-in dd1\", command_runner_.commands_ran_[0]);\n  EXPECT_EQ(\"cp dd0-in dd0\", command_runner_.commands_ran_[1]);\n  EXPECT_EQ(\"touch in\", command_runner_.commands_ran_[2]);\n  EXPECT_EQ(\"touch tmp\", command_runner_.commands_ran_[3]);\n  EXPECT_EQ(\"touch out\", command_runner_.commands_ran_[4]);\n}\n\nTEST_F(BuildTest, Validation) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"build out: cat in |@ validate\\n\"\n    \"build validate: cat in2\\n\"));\n\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"in2\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // Test touching \"in\" only rebuilds \"out\" (\"validate\" doesn't depend on\n  // \"out\").\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cat in > out\", command_runner_.commands_ran_[0]);\n\n  // Test touching \"in2\" only rebuilds \"validate\" (\"out\" doesn't depend on\n  // \"validate\").\n  fs_.Tick();\n  fs_.Create(\"in2\", \"\");\n\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cat in2 > validate\", command_runner_.commands_ran_[0]);\n}\n\nTEST_F(BuildTest, ValidationDependsOnOutput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"build out: cat in |@ validate\\n\"\n    \"build validate: cat in2 | out\\n\"));\n\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"in2\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // Test touching \"in\" rebuilds \"out\" and \"validate\".\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // Test touching \"in2\" only rebuilds \"validate\" (\"out\" doesn't depend on\n  // \"validate\").\n  fs_.Tick();\n  fs_.Create(\"in2\", \"\");\n\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cat in2 > validate\", command_runner_.commands_ran_[0]);\n}\n\nTEST_F(BuildWithDepsLogTest, ValidationThroughDepfile) {\n  const char* manifest =\n      \"build out: cat in |@ validate\\n\"\n      \"build validate: cat in2 | out\\n\"\n      \"build out2: cat in3\\n\"\n      \"  deps = gcc\\n\"\n      \"  depfile = out2.d\\n\";\n\n  string err;\n\n  {\n    fs_.Create(\"in\", \"\");\n    fs_.Create(\"in2\", \"\");\n    fs_.Create(\"in3\", \"\");\n    fs_.Create(\"out2.d\", \"out: out\");\n\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    builder.command_runner_.reset(&command_runner_);\n    SafeRelease protect(&builder);\n\n    EXPECT_TRUE(builder.AddTarget(\"out2\", &err));\n    ASSERT_EQ(\"\", err);\n\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    // On the first build, only the out2 command is run.\n    ASSERT_EQ(command_runner_.commands_ran_.size(), size_t(1));\n    EXPECT_EQ(\"cat in3 > out2\", command_runner_.commands_ran_[0]);\n\n    // The deps file should have been removed.\n    EXPECT_EQ(0, fs_.Stat(\"out2.d\", &err));\n\n    deps_log.Close();\n  }\n\n  fs_.Tick();\n  command_runner_.commands_ran_.clear();\n\n  {\n    fs_.Create(\"in2\", \"\");\n    fs_.Create(\"in3\", \"\");\n\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));\n\n    DepsLog deps_log;\n    ASSERT_TRUE(deps_log.Load(deps_log_file_.path(), &state, &err));\n    ASSERT_TRUE(deps_log.OpenForWrite(deps_log_file_.path(), &err));\n    ASSERT_EQ(\"\", err);\n\n    Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);\n    SafeRelease ff(&builder);\n    builder.command_runner_.reset(&command_runner_);\n\n    EXPECT_TRUE(builder.AddTarget(\"out2\", &err));\n    ASSERT_EQ(\"\", err);\n\n    EXPECT_EQ(builder.Build(&err), ExitSuccess);\n    EXPECT_EQ(\"\", err);\n\n    // The out and validate actions should have been run as well as out2.\n    ASSERT_EQ(command_runner_.commands_ran_.size(), size_t(3));\n    // out has to run first, as both out2 and validate depend on it.\n    EXPECT_EQ(\"cat in > out\", command_runner_.commands_ran_[0]);\n\n    deps_log.Close();\n  }\n}\n\nTEST_F(BuildTest, ValidationCircular) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"build out: cat in |@ out2\\n\"\n    \"build out2: cat in2 |@ out\\n\"));\n\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"in2\", \"\");\n\n  string err;\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(2u, command_runner_.commands_ran_.size());\n\n  // Test touching \"in\" rebuilds \"out\".\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cat in > out\", command_runner_.commands_ran_[0]);\n\n  // Test touching \"in2\" rebuilds \"out2\".\n  fs_.Tick();\n  fs_.Create(\"in2\", \"\");\n\n  err.clear();\n  command_runner_.commands_ran_.clear();\n  state_.Reset();\n  EXPECT_TRUE(builder_.AddTarget(\"out\", &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(builder_.Build(&err), ExitSuccess);\n  EXPECT_EQ(\"\", err);\n\n  ASSERT_EQ(1u, command_runner_.commands_ran_.size());\n  EXPECT_EQ(\"cat in2 > out2\", command_runner_.commands_ran_[0]);\n}\n\nTEST_F(BuildTest, ValidationWithCircularDependency) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n    \"build out: cat in |@ validate\\n\"\n    \"build validate: cat validate_in | out\\n\"\n    \"build validate_in: cat validate\\n\"));\n\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_FALSE(builder_.AddTarget(\"out\", &err));\n  EXPECT_EQ(\"dependency cycle: validate -> validate_in -> validate\", err);\n}\n"
  },
  {
    "path": "src/canon_perftest.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"util.h\"\n#include \"metrics.h\"\n\nusing namespace std;\n\nconst char kPath[] =\n    \"../../third_party/WebKit/Source/WebCore/\"\n    \"platform/leveldb/LevelDBWriteBatch.cpp\";\n\nint main() {\n  vector<int> times;\n\n  char buf[200];\n  size_t len = strlen(kPath);\n  strcpy(buf, kPath);\n\n  for (int j = 0; j < 5; ++j) {\n    const int kNumRepetitions = 2000000;\n    int64_t start = GetTimeMillis();\n    uint64_t slash_bits;\n    for (int i = 0; i < kNumRepetitions; ++i) {\n      CanonicalizePath(buf, &len, &slash_bits);\n    }\n    int delta = (int)(GetTimeMillis() - start);\n    times.push_back(delta);\n  }\n\n  int min = times[0];\n  int max = times[0];\n  float total = 0;\n  for (size_t i = 0; i < times.size(); ++i) {\n    total += times[i];\n    if (times[i] < min)\n      min = times[i];\n    else if (times[i] > max)\n      max = times[i];\n  }\n\n  printf(\"min %dms  max %dms  avg %.1fms\\n\",\n         min, max, total / times.size());\n}\n"
  },
  {
    "path": "src/clean.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"clean.h\"\n\n#include <assert.h>\n#include <stdio.h>\n\n#include \"disk_interface.h\"\n#include \"graph.h\"\n#include \"state.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nCleaner::Cleaner(State* state,\n                 const BuildConfig& config,\n                 DiskInterface* disk_interface)\n  : state_(state),\n    config_(config),\n    dyndep_loader_(state, disk_interface),\n    cleaned_files_count_(0),\n    disk_interface_(disk_interface),\n    status_(0) {\n}\n\nint Cleaner::RemoveFile(const string& path) {\n  return disk_interface_->RemoveFile(path);\n}\n\nbool Cleaner::FileExists(const string& path) {\n  string err;\n  TimeStamp mtime = disk_interface_->Stat(path, &err);\n  if (mtime == -1)\n    Error(\"%s\", err.c_str());\n  return mtime > 0;  // Treat Stat() errors as \"file does not exist\".\n}\n\nvoid Cleaner::Report(const string& path) {\n  ++cleaned_files_count_;\n  if (IsVerbose())\n    printf(\"Remove %s\\n\", path.c_str());\n}\n\nvoid Cleaner::Remove(const string& path) {\n  if (!IsAlreadyRemoved(path)) {\n    removed_.insert(path);\n    if (config_.dry_run) {\n      if (FileExists(path))\n        Report(path);\n    } else {\n      int ret = RemoveFile(path);\n      if (ret == 0)\n        Report(path);\n      else if (ret == -1)\n        status_ = 1;\n    }\n  }\n}\n\nbool Cleaner::IsAlreadyRemoved(const string& path) {\n  set<string>::iterator i = removed_.find(path);\n  return (i != removed_.end());\n}\n\nvoid Cleaner::RemoveEdgeFiles(Edge* edge) {\n  string depfile = edge->GetUnescapedDepfile();\n  if (!depfile.empty())\n    Remove(depfile);\n\n  string rspfile = edge->GetUnescapedRspfile();\n  if (!rspfile.empty())\n    Remove(rspfile);\n}\n\nvoid Cleaner::PrintHeader() {\n  if (config_.verbosity == BuildConfig::QUIET)\n    return;\n  printf(\"Cleaning...\");\n  if (IsVerbose())\n    printf(\"\\n\");\n  else\n    printf(\" \");\n  fflush(stdout);\n}\n\nvoid Cleaner::PrintFooter() {\n  if (config_.verbosity == BuildConfig::QUIET)\n    return;\n  printf(\"%d files.\\n\", cleaned_files_count_);\n}\n\nint Cleaner::CleanAll(bool generator) {\n  Reset();\n  PrintHeader();\n  LoadDyndeps();\n  for (vector<Edge*>::iterator e = state_->edges_.begin();\n       e != state_->edges_.end(); ++e) {\n    // Do not try to remove phony targets\n    if ((*e)->is_phony())\n      continue;\n    // Do not remove generator's files unless generator specified.\n    if (!generator && (*e)->GetBindingBool(\"generator\"))\n      continue;\n    for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();\n         out_node != (*e)->outputs_.end(); ++out_node) {\n      Remove((*out_node)->path());\n    }\n\n    RemoveEdgeFiles(*e);\n  }\n  PrintFooter();\n  return status_;\n}\n\nint Cleaner::CleanDead(const BuildLog::Entries& entries) {\n  Reset();\n  PrintHeader();\n  LoadDyndeps();\n  for (BuildLog::Entries::const_iterator i = entries.begin(); i != entries.end(); ++i) {\n    Node* n = state_->LookupNode(i->first);\n    // Detecting stale outputs works as follows:\n    //\n    // - If it has no Node, it is not in the build graph, or the deps log\n    //   anymore, hence is stale.\n    //\n    // - If it isn't an output or input for any edge, it comes from a stale\n    //   entry in the deps log, but no longer referenced from the build\n    //   graph.\n    //\n    if (!n || (!n->in_edge() && n->out_edges().empty())) {\n      Remove(i->first.AsString());\n    }\n  }\n  PrintFooter();\n  return status_;\n}\n\nvoid Cleaner::DoCleanTarget(Node* target) {\n  if (Edge* e = target->in_edge()) {\n    // Do not try to remove phony targets\n    if (!e->is_phony()) {\n      Remove(target->path());\n      RemoveEdgeFiles(e);\n    }\n    for (vector<Node*>::iterator n = e->inputs_.begin(); n != e->inputs_.end();\n         ++n) {\n      Node* next = *n;\n      // call DoCleanTarget recursively if this node has not been visited\n      if (cleaned_.count(next) == 0) {\n        DoCleanTarget(next);\n      }\n    }\n  }\n\n  // mark this target to be cleaned already\n  cleaned_.insert(target);\n}\n\nint Cleaner::CleanTarget(Node* target) {\n  assert(target);\n\n  Reset();\n  PrintHeader();\n  LoadDyndeps();\n  DoCleanTarget(target);\n  PrintFooter();\n  return status_;\n}\n\nint Cleaner::CleanTarget(const char* target) {\n  assert(target);\n\n  Reset();\n  Node* node = state_->LookupNode(target);\n  if (node) {\n    CleanTarget(node);\n  } else {\n    Error(\"unknown target '%s'\", target);\n    status_ = 1;\n  }\n  return status_;\n}\n\nint Cleaner::CleanTargets(int target_count, char* targets[]) {\n  Reset();\n  PrintHeader();\n  LoadDyndeps();\n  for (int i = 0; i < target_count; ++i) {\n    string target_name = targets[i];\n    if (target_name.empty()) {\n      Error(\"failed to canonicalize '': empty path\");\n      status_ = 1;\n      continue;\n    }\n    uint64_t slash_bits;\n    CanonicalizePath(&target_name, &slash_bits);\n    Node* target = state_->LookupNode(target_name);\n    if (target) {\n      if (IsVerbose())\n        printf(\"Target %s\\n\", target_name.c_str());\n      DoCleanTarget(target);\n    } else {\n      Error(\"unknown target '%s'\", target_name.c_str());\n      status_ = 1;\n    }\n  }\n  PrintFooter();\n  return status_;\n}\n\nvoid Cleaner::DoCleanRule(const Rule* rule) {\n  assert(rule);\n\n  for (vector<Edge*>::iterator e = state_->edges_.begin();\n       e != state_->edges_.end(); ++e) {\n    if ((*e)->rule().name() == rule->name()) {\n      for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();\n           out_node != (*e)->outputs_.end(); ++out_node) {\n        Remove((*out_node)->path());\n        RemoveEdgeFiles(*e);\n      }\n    }\n  }\n}\n\nint Cleaner::CleanRule(const Rule* rule) {\n  assert(rule);\n\n  Reset();\n  PrintHeader();\n  LoadDyndeps();\n  DoCleanRule(rule);\n  PrintFooter();\n  return status_;\n}\n\nint Cleaner::CleanRule(const char* rule) {\n  assert(rule);\n\n  Reset();\n  const Rule* r = state_->bindings_.LookupRule(rule);\n  if (r) {\n    CleanRule(r);\n  } else {\n    Error(\"unknown rule '%s'\", rule);\n    status_ = 1;\n  }\n  return status_;\n}\n\nint Cleaner::CleanRules(int rule_count, char* rules[]) {\n  assert(rules);\n\n  Reset();\n  PrintHeader();\n  LoadDyndeps();\n  for (int i = 0; i < rule_count; ++i) {\n    const char* rule_name = rules[i];\n    const Rule* rule = state_->bindings_.LookupRule(rule_name);\n    if (rule) {\n      if (IsVerbose())\n        printf(\"Rule %s\\n\", rule_name);\n      DoCleanRule(rule);\n    } else {\n      Error(\"unknown rule '%s'\", rule_name);\n      status_ = 1;\n    }\n  }\n  PrintFooter();\n  return status_;\n}\n\nvoid Cleaner::Reset() {\n  status_ = 0;\n  cleaned_files_count_ = 0;\n  removed_.clear();\n  cleaned_.clear();\n}\n\nvoid Cleaner::LoadDyndeps() {\n  // Load dyndep files that exist, before they are cleaned.\n  for (vector<Edge*>::iterator e = state_->edges_.begin();\n       e != state_->edges_.end(); ++e) {\n    Node* dyndep;\n    if ((dyndep = (*e)->dyndep_) && dyndep->dyndep_pending()) {\n      // Capture and ignore errors loading the dyndep file.\n      // We clean as much of the graph as we know.\n      std::string err;\n      dyndep_loader_.LoadDyndeps(dyndep, &err);\n    }\n  }\n}\n"
  },
  {
    "path": "src/clean.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_CLEAN_H_\n#define NINJA_CLEAN_H_\n\n#include <set>\n#include <string>\n\n#include \"build.h\"\n#include \"dyndep.h\"\n#include \"build_log.h\"\n\nstruct State;\nstruct Node;\nstruct Rule;\nstruct DiskInterface;\n\nstruct Cleaner {\n  /// Build a cleaner object with the given @a disk_interface\n  Cleaner(State* state,\n          const BuildConfig& config,\n          DiskInterface* disk_interface);\n\n  /// Clean the given @a target and all the file built for it.\n  /// @return non-zero if an error occurs.\n  int CleanTarget(Node* target);\n  /// Clean the given target @a target.\n  /// @return non-zero if an error occurs.\n  int CleanTarget(const char* target);\n  /// Clean the given target @a targets.\n  /// @return non-zero if an error occurs.\n  int CleanTargets(int target_count, char* targets[]);\n\n  /// Clean all built files, except for files created by generator rules.\n  /// @param generator If set, also clean files created by generator rules.\n  /// @return non-zero if an error occurs.\n  int CleanAll(bool generator = false);\n\n  /// Clean all the file built with the given rule @a rule.\n  /// @return non-zero if an error occurs.\n  int CleanRule(const Rule* rule);\n  /// Clean the file produced by the given @a rule.\n  /// @return non-zero if an error occurs.\n  int CleanRule(const char* rule);\n  /// Clean the file produced by the given @a rules.\n  /// @return non-zero if an error occurs.\n  int CleanRules(int rule_count, char* rules[]);\n  /// Clean the files produced by previous builds that are no longer in the\n  /// manifest.\n  /// @return non-zero if an error occurs.\n  int CleanDead(const BuildLog::Entries& entries);\n\n  /// @return the number of file cleaned.\n  int cleaned_files_count() const {\n    return cleaned_files_count_;\n  }\n\n  /// @return whether the cleaner is in verbose mode.\n  bool IsVerbose() const {\n    return (config_.verbosity != BuildConfig::QUIET\n            && (config_.verbosity == BuildConfig::VERBOSE || config_.dry_run));\n  }\n\n private:\n  /// Remove the file @a path.\n  /// @return whether the file has been removed.\n  int RemoveFile(const std::string& path);\n  /// @returns whether the file @a path exists.\n  bool FileExists(const std::string& path);\n  void Report(const std::string& path);\n\n  /// Remove the given @a path file only if it has not been already removed.\n  void Remove(const std::string& path);\n  /// @return whether the given @a path has already been removed.\n  bool IsAlreadyRemoved(const std::string& path);\n  /// Remove the depfile and rspfile for an Edge.\n  void RemoveEdgeFiles(Edge* edge);\n\n  /// Helper recursive method for CleanTarget().\n  void DoCleanTarget(Node* target);\n  void PrintHeader();\n  void PrintFooter();\n  void DoCleanRule(const Rule* rule);\n  void Reset();\n\n  /// Load dependencies from dyndep bindings.\n  void LoadDyndeps();\n\n  State* state_;\n  const BuildConfig& config_;\n  DyndepLoader dyndep_loader_;\n  std::set<std::string> removed_;\n  std::set<Node*> cleaned_;\n  int cleaned_files_count_;\n  DiskInterface* disk_interface_;\n  int status_;\n};\n\n#endif  // NINJA_CLEAN_H_\n"
  },
  {
    "path": "src/clean_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"clean.h\"\n#include \"build.h\"\n\n#include \"util.h\"\n#include \"test.h\"\n\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\nusing namespace std;\n\nnamespace {\n\nconst char kTestFilename[] = \"CleanTest-tempfile\";\n\nstruct CleanTest : public StateTestWithBuiltinRules {\n  VirtualFileSystem fs_;\n  BuildConfig config_;\n  virtual void SetUp() {\n    config_.verbosity = BuildConfig::QUIET;\n  }\n};\n\nTEST_F(CleanTest, CleanAll) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build in1: cat src1\\n\"\n\"build out1: cat in1\\n\"\n\"build in2: cat src2\\n\"\n\"build out2: cat in2\\n\"));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"in2\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(4, cleaner.cleaned_files_count());\n  EXPECT_EQ(4u, fs_.files_removed_.size());\n\n  // Check they are removed.\n  string err;\n  EXPECT_EQ(0, fs_.Stat(\"in1\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out1\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"in2\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out2\", &err));\n  fs_.files_removed_.clear();\n\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(0, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanAllDryRun) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build in1: cat src1\\n\"\n\"build out1: cat in1\\n\"\n\"build in2: cat src2\\n\"\n\"build out2: cat in2\\n\"));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"in2\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  config_.dry_run = true;\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(4, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n\n  // Check they are not removed.\n  string err;\n  EXPECT_LT(0, fs_.Stat(\"in1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"in2\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out2\", &err));\n  fs_.files_removed_.clear();\n\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(4, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanTarget) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build in1: cat src1\\n\"\n\"build out1: cat in1\\n\"\n\"build in2: cat src2\\n\"\n\"build out2: cat in2\\n\"));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"in2\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  ASSERT_EQ(0, cleaner.CleanTarget(\"out1\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n\n  // Check they are removed.\n  string err;\n  EXPECT_EQ(0, fs_.Stat(\"in1\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"in2\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out2\", &err));\n  fs_.files_removed_.clear();\n\n  ASSERT_EQ(0, cleaner.CleanTarget(\"out1\"));\n  EXPECT_EQ(0, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanTargetDryRun) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build in1: cat src1\\n\"\n\"build out1: cat in1\\n\"\n\"build in2: cat src2\\n\"\n\"build out2: cat in2\\n\"));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"in2\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  config_.dry_run = true;\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  ASSERT_EQ(0, cleaner.CleanTarget(\"out1\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n\n  // Check they are not removed.\n  string err;\n  EXPECT_LT(0, fs_.Stat(\"in1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"in2\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out2\", &err));\n  fs_.files_removed_.clear();\n\n  ASSERT_EQ(0, cleaner.CleanTarget(\"out1\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanRule) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cat_e\\n\"\n\"  command = cat -e $in > $out\\n\"\n\"build in1: cat_e src1\\n\"\n\"build out1: cat in1\\n\"\n\"build in2: cat_e src2\\n\"\n\"build out2: cat in2\\n\"));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"in2\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  ASSERT_EQ(0, cleaner.CleanRule(\"cat_e\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n\n  // Check they are removed.\n  string err;\n  EXPECT_EQ(0, fs_.Stat(\"in1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out1\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"in2\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out2\", &err));\n  fs_.files_removed_.clear();\n\n  ASSERT_EQ(0, cleaner.CleanRule(\"cat_e\"));\n  EXPECT_EQ(0, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanRuleDryRun) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cat_e\\n\"\n\"  command = cat -e $in > $out\\n\"\n\"build in1: cat_e src1\\n\"\n\"build out1: cat in1\\n\"\n\"build in2: cat_e src2\\n\"\n\"build out2: cat in2\\n\"));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"in2\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  config_.dry_run = true;\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  ASSERT_EQ(0, cleaner.CleanRule(\"cat_e\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n\n  // Check they are not removed.\n  string err;\n  EXPECT_LT(0, fs_.Stat(\"in1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out1\", &err));\n  EXPECT_LT(0, fs_.Stat(\"in2\", &err));\n  EXPECT_LT(0, fs_.Stat(\"out2\", &err));\n  fs_.files_removed_.clear();\n\n  ASSERT_EQ(0, cleaner.CleanRule(\"cat_e\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanRuleGenerator) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule regen\\n\"\n\"  command = cat $in > $out\\n\"\n\"  generator = 1\\n\"\n\"build out1: cat in1\\n\"\n\"build out2: regen in2\\n\"));\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(1, cleaner.cleaned_files_count());\n  EXPECT_EQ(1u, fs_.files_removed_.size());\n\n  fs_.Create(\"out1\", \"\");\n\n  EXPECT_EQ(0, cleaner.CleanAll(/*generator=*/true));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanDepFile) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n\"\n\"  command = cc $in > $out\\n\"\n\"  depfile = $out.d\\n\"\n\"build out1: cc in1\\n\"));\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out1.d\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanDepFileOnCleanTarget) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n\"\n\"  command = cc $in > $out\\n\"\n\"  depfile = $out.d\\n\"\n\"build out1: cc in1\\n\"));\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out1.d\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner.CleanTarget(\"out1\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanDepFileOnCleanRule) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n\"\n\"  command = cc $in > $out\\n\"\n\"  depfile = $out.d\\n\"\n\"build out1: cc in1\\n\"));\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out1.d\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner.CleanRule(\"cc\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanDyndep) {\n  // Verify that a dyndep file can be loaded to discover a new output\n  // to be cleaned.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat in || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out | out.imp: dyndep\\n\"\n);\n  fs_.Create(\"out\", \"\");\n  fs_.Create(\"out.imp\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n\n  string err;\n  EXPECT_EQ(0, fs_.Stat(\"out\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out.imp\", &err));\n}\n\nTEST_F(CleanTest, CleanDyndepMissing) {\n  // Verify that a missing dyndep file is tolerated.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat in || dd\\n\"\n\"  dyndep = dd\\n\"\n  ));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out\", \"\");\n  fs_.Create(\"out.imp\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(1, cleaner.cleaned_files_count());\n  EXPECT_EQ(1u, fs_.files_removed_.size());\n\n  string err;\n  EXPECT_EQ(0, fs_.Stat(\"out\", &err));\n  EXPECT_EQ(1, fs_.Stat(\"out.imp\", &err));\n}\n\nTEST_F(CleanTest, CleanRspFile) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc\\n\"\n\"  command = cc $in > $out\\n\"\n\"  rspfile = $rspfile\\n\"\n\"  rspfile_content=$in\\n\"\n\"build out1: cc in1\\n\"\n\"  rspfile = cc1.rsp\\n\"));\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"cc1.rsp\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_EQ(2u, fs_.files_removed_.size());\n}\n\nTEST_F(CleanTest, CleanRsp) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cat_rsp \\n\"\n\"  command = cat $rspfile > $out\\n\"\n\"  rspfile = $rspfile\\n\"\n\"  rspfile_content = $in\\n\"\n\"build in1: cat src1\\n\"\n\"build out1: cat in1\\n\"\n\"build in2: cat_rsp src2\\n\"\n\"  rspfile=in2.rsp\\n\"\n\"build out2: cat_rsp in2\\n\"\n\"  rspfile=out2.rsp\\n\"\n));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"in2.rsp\", \"\");\n  fs_.Create(\"out2.rsp\", \"\");\n  fs_.Create(\"in2\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n  ASSERT_EQ(0, cleaner.cleaned_files_count());\n  ASSERT_EQ(0, cleaner.CleanTarget(\"out1\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  ASSERT_EQ(0, cleaner.CleanTarget(\"in2\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  ASSERT_EQ(0, cleaner.CleanRule(\"cat_rsp\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n\n  EXPECT_EQ(6u, fs_.files_removed_.size());\n\n  // Check they are removed.\n  string err;\n  EXPECT_EQ(0, fs_.Stat(\"in1\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out1\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"in2\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out2\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"in2.rsp\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out2.rsp\", &err));\n}\n\nTEST_F(CleanTest, CleanFailure) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n                                      \"build dir: cat src1\\n\"));\n  fs_.MakeDir(\"dir\");\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_NE(0, cleaner.CleanAll());\n}\n\nTEST_F(CleanTest, CleanPhony) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build phony: phony t1 t2\\n\"\n\"build t1: cat\\n\"\n\"build t2: cat\\n\"));\n\n  fs_.Create(\"phony\", \"\");\n  fs_.Create(\"t1\", \"\");\n  fs_.Create(\"t2\", \"\");\n\n  // Check that CleanAll does not remove \"phony\".\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_LT(0, fs_.Stat(\"phony\", &err));\n\n  fs_.Create(\"t1\", \"\");\n  fs_.Create(\"t2\", \"\");\n\n  // Check that CleanTarget does not remove \"phony\".\n  EXPECT_EQ(0, cleaner.CleanTarget(\"phony\"));\n  EXPECT_EQ(2, cleaner.cleaned_files_count());\n  EXPECT_LT(0, fs_.Stat(\"phony\", &err));\n}\n\nTEST_F(CleanTest, CleanDepFileAndRspFileWithSpaces) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule cc_dep\\n\"\n\"  command = cc $in > $out\\n\"\n\"  depfile = $out.d\\n\"\n\"rule cc_rsp\\n\"\n\"  command = cc $in > $out\\n\"\n\"  rspfile = $out.rsp\\n\"\n\"  rspfile_content = $in\\n\"\n\"build out$ 1: cc_dep in$ 1\\n\"\n\"build out$ 2: cc_rsp in$ 1\\n\"\n));\n  fs_.Create(\"out 1\", \"\");\n  fs_.Create(\"out 2\", \"\");\n  fs_.Create(\"out 1.d\", \"\");\n  fs_.Create(\"out 2.rsp\", \"\");\n\n  Cleaner cleaner(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner.CleanAll());\n  EXPECT_EQ(4, cleaner.cleaned_files_count());\n  EXPECT_EQ(4u, fs_.files_removed_.size());\n\n  string err;\n  EXPECT_EQ(0, fs_.Stat(\"out 1\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out 2\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out 1.d\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out 2.rsp\", &err));\n}\n\nstruct CleanDeadTest : public CleanTest, public BuildLogUser{\n  virtual void SetUp() {\n    // In case a crashing test left a stale file behind.\n    platformAwareUnlink(kTestFilename);\n    CleanTest::SetUp();\n  }\n  virtual void TearDown() {\n    platformAwareUnlink(kTestFilename);\n  }\n  virtual bool IsPathDead(StringPiece) const { return false; }\n};\n\nTEST_F(CleanDeadTest, CleanDead) {\n  State state;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state,\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build out1: cat in\\n\"\n\"build out2: cat in\\n\"\n));\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out2: cat in\\n\"\n));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  BuildLog log1;\n  string err;\n  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));\n  ASSERT_EQ(\"\", err);\n  log1.RecordCommand(state.edges_[0], 15, 18);\n  log1.RecordCommand(state.edges_[1], 20, 25);\n  log1.Close();\n\n  BuildLog log2;\n  EXPECT_TRUE(log2.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(2u, log2.entries().size());\n  ASSERT_TRUE(log2.LookupByOutput(\"out1\"));\n  ASSERT_TRUE(log2.LookupByOutput(\"out2\"));\n\n  // First use the manifest that describe how to build out1.\n  Cleaner cleaner1(&state, config_, &fs_);\n  EXPECT_EQ(0, cleaner1.CleanDead(log2.entries()));\n  EXPECT_EQ(0, cleaner1.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n  EXPECT_NE(0, fs_.Stat(\"in\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out1\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out2\", &err));\n\n  // Then use the manifest that does not build out1 anymore.\n  Cleaner cleaner2(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner2.CleanDead(log2.entries()));\n  EXPECT_EQ(1, cleaner2.cleaned_files_count());\n  EXPECT_EQ(1u, fs_.files_removed_.size());\n  EXPECT_EQ(\"out1\", *(fs_.files_removed_.begin()));\n  EXPECT_NE(0, fs_.Stat(\"in\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out1\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out2\", &err));\n\n  // Nothing to do now.\n  EXPECT_EQ(0, cleaner2.CleanDead(log2.entries()));\n  EXPECT_EQ(0, cleaner2.cleaned_files_count());\n  EXPECT_EQ(1u, fs_.files_removed_.size());\n  EXPECT_EQ(\"out1\", *(fs_.files_removed_.begin()));\n  EXPECT_NE(0, fs_.Stat(\"in\", &err));\n  EXPECT_EQ(0, fs_.Stat(\"out1\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out2\", &err));\n  log2.Close();\n}\n\nTEST_F(CleanDeadTest, CleanDeadPreservesInputs) {\n  State state;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state,\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build out1: cat in\\n\"\n\"build out2: cat in\\n\"\n));\n  // This manifest does not build out1 anymore, but makes\n  // it an implicit input. CleanDead should detect this\n  // and preserve it.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out2: cat in | out1\\n\"\n));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out1\", \"\");\n  fs_.Create(\"out2\", \"\");\n\n  BuildLog log1;\n  string err;\n  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));\n  ASSERT_EQ(\"\", err);\n  log1.RecordCommand(state.edges_[0], 15, 18);\n  log1.RecordCommand(state.edges_[1], 20, 25);\n  log1.Close();\n\n  BuildLog log2;\n  EXPECT_TRUE(log2.Load(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(2u, log2.entries().size());\n  ASSERT_TRUE(log2.LookupByOutput(\"out1\"));\n  ASSERT_TRUE(log2.LookupByOutput(\"out2\"));\n\n  // First use the manifest that describe how to build out1.\n  Cleaner cleaner1(&state, config_, &fs_);\n  EXPECT_EQ(0, cleaner1.CleanDead(log2.entries()));\n  EXPECT_EQ(0, cleaner1.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n  EXPECT_NE(0, fs_.Stat(\"in\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out1\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out2\", &err));\n\n  // Then use the manifest that does not build out1 anymore.\n  Cleaner cleaner2(&state_, config_, &fs_);\n  EXPECT_EQ(0, cleaner2.CleanDead(log2.entries()));\n  EXPECT_EQ(0, cleaner2.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n  EXPECT_NE(0, fs_.Stat(\"in\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out1\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out2\", &err));\n\n  // Nothing to do now.\n  EXPECT_EQ(0, cleaner2.CleanDead(log2.entries()));\n  EXPECT_EQ(0, cleaner2.cleaned_files_count());\n  EXPECT_EQ(0u, fs_.files_removed_.size());\n  EXPECT_NE(0, fs_.Stat(\"in\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out1\", &err));\n  EXPECT_NE(0, fs_.Stat(\"out2\", &err));\n  log2.Close();\n}\n}  // anonymous namespace\n"
  },
  {
    "path": "src/clparser.cc",
    "content": "// Copyright 2015 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"clparser.h\"\n\n#include <algorithm>\n#include <assert.h>\n#include <string.h>\n\n#include \"metrics.h\"\n#include \"string_piece_util.h\"\n\n#ifdef _WIN32\n#include \"includes_normalize.h\"\n#include \"string_piece.h\"\n#else\n#include \"util.h\"\n#endif\n\nusing namespace std;\n\nnamespace {\n\n/// Return true if \\a input ends with \\a needle.\nbool EndsWith(const string& input, const string& needle) {\n  return (input.size() >= needle.size() &&\n          input.substr(input.size() - needle.size()) == needle);\n}\n\n}  // anonymous namespace\n\n// static\nstring CLParser::FilterShowIncludes(const string& line,\n                                    const string& deps_prefix) {\n  const string kDepsPrefixEnglish = \"Note: including file: \";\n  const char* in = line.c_str();\n  const char* end = in + line.size();\n  const string& prefix = deps_prefix.empty() ? kDepsPrefixEnglish : deps_prefix;\n  if (end - in > (int)prefix.size() &&\n      memcmp(in, prefix.c_str(), (int)prefix.size()) == 0) {\n    in += prefix.size();\n    while (*in == ' ')\n      ++in;\n    return line.substr(in - line.c_str());\n  }\n  return \"\";\n}\n\n// static\nbool CLParser::IsSystemInclude(string path) {\n  transform(path.begin(), path.end(), path.begin(), ToLowerASCII);\n  // TODO: this is a heuristic, perhaps there's a better way?\n  return (path.find(\"program files\") != string::npos ||\n          path.find(\"microsoft visual studio\") != string::npos);\n}\n\n// static\nbool CLParser::FilterInputFilename(string line) {\n  transform(line.begin(), line.end(), line.begin(), ToLowerASCII);\n  // TODO: other extensions, like .asm?\n  return EndsWith(line, \".c\") ||\n      EndsWith(line, \".cc\") ||\n      EndsWith(line, \".cxx\") ||\n      EndsWith(line, \".cpp\") ||\n      EndsWith(line, \".c++\");\n}\n\n// static\nbool CLParser::Parse(const string& output, const string& deps_prefix,\n                     string* filtered_output, string* err) {\n  METRIC_RECORD(\"CLParser::Parse\");\n\n  // Loop over all lines in the output to process them.\n  assert(&output != filtered_output);\n  size_t start = 0;\n  bool seen_show_includes = false;\n#ifdef _WIN32\n  IncludesNormalize normalizer(\".\");\n#endif\n\n  while (start < output.size()) {\n    size_t end = output.find_first_of(\"\\r\\n\", start);\n    if (end == string::npos)\n      end = output.size();\n    string line = output.substr(start, end - start);\n\n    string include = FilterShowIncludes(line, deps_prefix);\n    if (!include.empty()) {\n      seen_show_includes = true;\n      string normalized;\n#ifdef _WIN32\n      if (!normalizer.Normalize(include, &normalized, err))\n        return false;\n#else\n      // TODO: should this make the path relative to cwd?\n      normalized = include;\n      uint64_t slash_bits;\n      CanonicalizePath(&normalized, &slash_bits);\n#endif\n      if (!IsSystemInclude(normalized))\n        includes_.insert(normalized);\n    } else if (!seen_show_includes && FilterInputFilename(line)) {\n      // Drop it.\n      // TODO: if we support compiling multiple output files in a single\n      // cl.exe invocation, we should stash the filename.\n    } else {\n      filtered_output->append(line);\n      filtered_output->append(\"\\n\");\n    }\n\n    if (end < output.size() && output[end] == '\\r')\n      ++end;\n    if (end < output.size() && output[end] == '\\n')\n      ++end;\n    start = end;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "src/clparser.h",
    "content": "// Copyright 2015 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_CLPARSER_H_\n#define NINJA_CLPARSER_H_\n\n#include <set>\n#include <string>\n\n/// Visual Studio's cl.exe requires some massaging to work with Ninja;\n/// for example, it emits include information on stderr in a funny\n/// format when building with /showIncludes.  This class parses this\n/// output.\nstruct CLParser {\n  /// Parse a line of cl.exe output and extract /showIncludes info.\n  /// If a dependency is extracted, returns a nonempty string.\n  /// Exposed for testing.\n  static std::string FilterShowIncludes(const std::string& line,\n                                        const std::string& deps_prefix);\n\n  /// Return true if a mentioned include file is a system path.\n  /// Filtering these out reduces dependency information considerably.\n  static bool IsSystemInclude(std::string path);\n\n  /// Parse a line of cl.exe output and return true if it looks like\n  /// it's printing an input filename.  This is a heuristic but it appears\n  /// to be the best we can do.\n  /// Exposed for testing.\n  static bool FilterInputFilename(std::string line);\n\n  /// Parse the full output of cl, filling filtered_output with the text that\n  /// should be printed (if any). Returns true on success, or false with err\n  /// filled. output must not be the same object as filtered_object.\n  bool Parse(const std::string& output, const std::string& deps_prefix,\n             std::string* filtered_output, std::string* err);\n\n  std::set<std::string> includes_;\n};\n\n#endif  // NINJA_CLPARSER_H_\n"
  },
  {
    "path": "src/clparser_perftest.cc",
    "content": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"clparser.h\"\n#include \"metrics.h\"\n\nusing namespace std;\n\nint main(int argc, char* argv[]) {\n  // Output of /showIncludes from #include <iostream>\n  string perf_testdata =\n      \"Note: including file: C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\iostream\\r\\n\"\n      \"Note: including file:  C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\istream\\r\\n\"\n      \"Note: including file:   C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\ostream\\r\\n\"\n      \"Note: including file:    C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\ios\\r\\n\"\n      \"Note: including file:     C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xlocnum\\r\\n\"\n      \"Note: including file:      C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\climits\\r\\n\"\n      \"Note: including file:       C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\yvals.h\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xkeycheck.h\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\crtdefs.h\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\sal.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\ConcurrencySal.h\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vadefs.h\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt.h\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\use_ansi.h\\r\\n\"\n      \"Note: including file:       C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\limits.h\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:      C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cmath\\r\\n\"\n      \"Note: including file:       C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\math.h\\r\\n\"\n      \"Note: including file:       C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xtgmath.h\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xtr1common\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cstdlib\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\stdlib.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_malloc.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_search.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\stddef.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wstdlib.h\\r\\n\"\n      \"Note: including file:      C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cstdio\\r\\n\"\n      \"Note: including file:       C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\stdio.h\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wstdio.h\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_stdio_config.h\\r\\n\"\n      \"Note: including file:      C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\streambuf\\r\\n\"\n      \"Note: including file:       C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xiosbase\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xlocale\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cstring\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\string.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_memory.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_memcpy_s.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\errno.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime_string.h\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wstring.h\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\stdexcept\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\exception\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\type_traits\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xstddef\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cstddef\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\initializer_list\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\malloc.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime_exception.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\eh.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_terminate.h\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xstring\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xmemory0\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cstdint\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\stdint.h\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\limits\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\ymath.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cfloat\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\float.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cwchar\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\wchar.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wconio.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wctype.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wdirect.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wio.h\\r\\n\"\n      \"Note: including file:                C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_share.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wprocess.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\corecrt_wtime.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\sys/stat.h\\r\\n\"\n      \"Note: including file:                C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\sys/types.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\new\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime_new.h\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xutility\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\utility\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\iosfwd\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\crtdbg.h\\r\\n\"\n      \"Note: including file:                C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime_new_debug.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xatomic0.h\\r\\n\"\n      \"Note: including file:            C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\intrin.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\setjmp.h\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\immintrin.h\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\wmmintrin.h\\r\\n\"\n      \"Note: including file:               C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\nmmintrin.h\\r\\n\"\n      \"Note: including file:                C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\smmintrin.h\\r\\n\"\n      \"Note: including file:                 C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\tmmintrin.h\\r\\n\"\n      \"Note: including file:                  C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\pmmintrin.h\\r\\n\"\n      \"Note: including file:                   C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\emmintrin.h\\r\\n\"\n      \"Note: including file:                    C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xmmintrin.h\\r\\n\"\n      \"Note: including file:                     C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\mmintrin.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\ammintrin.h\\r\\n\"\n      \"Note: including file:             C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\mm3dnow.h\\r\\n\"\n      \"Note: including file:              C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\typeinfo\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime_typeinfo.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\vcruntime.h\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xlocinfo\\r\\n\"\n      \"Note: including file:          C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xlocinfo.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\ctype.h\\r\\n\"\n      \"Note: including file:           C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\locale.h\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\xfacet\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\system_error\\r\\n\"\n      \"Note: including file:         C:\\\\Program Files (x86)\\\\Microsoft Visual Studio 14.0\\\\VC\\\\INCLUDE\\\\cerrno\\r\\n\"\n      \"Note: including file:        C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\include\\\\10.0.10240.0\\\\ucrt\\\\share.h\\r\\n\";\n\n  for (int limit = 1 << 10; limit < (1<<20); limit *= 2) {\n    int64_t start = GetTimeMillis();\n    for (int rep = 0; rep < limit; ++rep) {\n      string output;\n      string err;\n\n      CLParser parser;\n      if (!parser.Parse(perf_testdata, \"\", &output, &err)) {\n        printf(\"%s\\n\", err.c_str());\n        return 1;\n      }\n    }\n    int64_t end = GetTimeMillis();\n\n    if (end - start > 2000) {\n      int delta_ms = (int)(end - start);\n      printf(\"Parse %d times in %dms avg %.1fus\\n\",\n             limit, delta_ms, float(delta_ms * 1000) / limit);\n      break;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "src/clparser_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"clparser.h\"\n\n#include \"test.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nTEST(CLParserTest, ShowIncludes) {\n  ASSERT_EQ(\"\", CLParser::FilterShowIncludes(\"\", \"\"));\n\n  ASSERT_EQ(\"\", CLParser::FilterShowIncludes(\"Sample compiler output\", \"\"));\n  ASSERT_EQ(\"c:\\\\Some Files\\\\foobar.h\",\n            CLParser::FilterShowIncludes(\"Note: including file: \"\n                                         \"c:\\\\Some Files\\\\foobar.h\", \"\"));\n  ASSERT_EQ(\"c:\\\\initspaces.h\",\n            CLParser::FilterShowIncludes(\"Note: including file:    \"\n                                         \"c:\\\\initspaces.h\", \"\"));\n  ASSERT_EQ(\"c:\\\\initspaces.h\",\n            CLParser::FilterShowIncludes(\"Non-default prefix: inc file:    \"\n                                         \"c:\\\\initspaces.h\",\n                    \"Non-default prefix: inc file:\"));\n}\n\nTEST(CLParserTest, FilterInputFilename) {\n  ASSERT_TRUE(CLParser::FilterInputFilename(\"foobar.cc\"));\n  ASSERT_TRUE(CLParser::FilterInputFilename(\"foo bar.cc\"));\n  ASSERT_TRUE(CLParser::FilterInputFilename(\"baz.c\"));\n  ASSERT_TRUE(CLParser::FilterInputFilename(\"FOOBAR.CC\"));\n\n  ASSERT_FALSE(CLParser::FilterInputFilename(\n                   \"src\\\\cl_helper.cc(166) : fatal error C1075: end \"\n                   \"of file found ...\"));\n}\n\nTEST(CLParserTest, ParseSimple) {\n  CLParser parser;\n  string output, err;\n  ASSERT_TRUE(parser.Parse(\n      \"foo\\r\\n\"\n      \"Note: inc file prefix:  foo.h\\r\\n\"\n      \"bar\\r\\n\",\n      \"Note: inc file prefix:\", &output, &err));\n\n  ASSERT_EQ(\"foo\\nbar\\n\", output);\n  ASSERT_EQ(1u, parser.includes_.size());\n  ASSERT_EQ(\"foo.h\", *parser.includes_.begin());\n}\n\nTEST(CLParserTest, ParseFilenameFilter) {\n  CLParser parser;\n  string output, err;\n  ASSERT_TRUE(parser.Parse(\n      \"foo.cc\\r\\n\"\n      \"cl: warning\\r\\n\",\n      \"\", &output, &err));\n  ASSERT_EQ(\"cl: warning\\n\", output);\n}\n\nTEST(CLParserTest, NoFilenameFilterAfterShowIncludes) {\n  CLParser parser;\n  string output, err;\n  ASSERT_TRUE(parser.Parse(\n      \"foo.cc\\r\\n\"\n      \"Note: including file: foo.h\\r\\n\"\n      \"something something foo.cc\\r\\n\",\n      \"\", &output, &err));\n  ASSERT_EQ(\"something something foo.cc\\n\", output);\n}\n\nTEST(CLParserTest, ParseSystemInclude) {\n  CLParser parser;\n  string output, err;\n  ASSERT_TRUE(parser.Parse(\n      \"Note: including file: c:\\\\Program Files\\\\foo.h\\r\\n\"\n      \"Note: including file: d:\\\\Microsoft Visual Studio\\\\bar.h\\r\\n\"\n      \"Note: including file: path.h\\r\\n\",\n      \"\", &output, &err));\n  // We should have dropped the first two includes because they look like\n  // system headers.\n  ASSERT_EQ(\"\", output);\n  ASSERT_EQ(1u, parser.includes_.size());\n  ASSERT_EQ(\"path.h\", *parser.includes_.begin());\n}\n\nTEST(CLParserTest, DuplicatedHeader) {\n  CLParser parser;\n  string output, err;\n  ASSERT_TRUE(parser.Parse(\n      \"Note: including file: foo.h\\r\\n\"\n      \"Note: including file: bar.h\\r\\n\"\n      \"Note: including file: foo.h\\r\\n\",\n      \"\", &output, &err));\n  // We should have dropped one copy of foo.h.\n  ASSERT_EQ(\"\", output);\n  ASSERT_EQ(2u, parser.includes_.size());\n}\n\nTEST(CLParserTest, DuplicatedHeaderPathConverted) {\n  CLParser parser;\n  string output, err;\n\n  // This isn't inline in the Parse() call below because the #ifdef in\n  // a macro expansion would confuse MSVC2013's preprocessor.\n  const char kInput[] =\n      \"Note: including file: sub/./foo.h\\r\\n\"\n      \"Note: including file: bar.h\\r\\n\"\n#ifdef _WIN32\n      \"Note: including file: sub\\\\foo.h\\r\\n\";\n#else\n      \"Note: including file: sub/foo.h\\r\\n\";\n#endif\n  ASSERT_TRUE(parser.Parse(kInput, \"\", &output, &err));\n  // We should have dropped one copy of foo.h.\n  ASSERT_EQ(\"\", output);\n  ASSERT_EQ(2u, parser.includes_.size());\n}\n"
  },
  {
    "path": "src/command_collector.h",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_COMMAND_COLLECTOR_H_\n#define NINJA_COMMAND_COLLECTOR_H_\n\n#include <cassert>\n#include <unordered_set>\n#include <vector>\n\n#include \"graph.h\"\n\n/// Collects the transitive set of edges that lead into a given set\n/// of starting nodes. Used to implement the `compdb-targets` tool.\n///\n/// When collecting inputs, the outputs of phony edges are always ignored\n/// from the result, but are followed by the dependency walk.\n///\n/// Usage is:\n/// - Create instance.\n/// - Call CollectFrom() for each root node to collect edges from.\n/// - Call TakeResult() to retrieve the list of edges.\n///\nstruct CommandCollector {\n  void CollectFrom(const Node* node) {\n    assert(node);\n\n    if (!visited_nodes_.insert(node).second)\n      return;\n\n    Edge* edge = node->in_edge();\n    if (!edge || !visited_edges_.insert(edge).second)\n      return;\n\n    for (Node* input_node : edge->inputs_)\n      CollectFrom(input_node);\n\n    if (!edge->is_phony())\n      in_edges.push_back(edge);\n  }\n\n private:\n  std::unordered_set<const Node*> visited_nodes_;\n  std::unordered_set<Edge*> visited_edges_;\n\n  /// we use a vector to preserve order from requisites to their dependents.\n  /// This may help LSP server performance in languages that support modules,\n  /// but it also ensures that the output of `-t compdb-targets foo` is\n  /// consistent, which is useful in regression tests.\n public:\n  std::vector<Edge*> in_edges;\n};\n\n#endif  //  NINJA_COMMAND_COLLECTOR_H_\n"
  },
  {
    "path": "src/debug_flags.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdio.h>\n#include <map>\n#include <vector>\n#include <string>\n\n#include \"graph.h\"\n\nbool g_explaining = false;\n\nbool g_keep_depfile = false;\n\nbool g_keep_rsp = false;\n\nbool g_experimental_statcache = true;\n"
  },
  {
    "path": "src/debug_flags.h",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_EXPLAIN_H_\n#define NINJA_EXPLAIN_H_\n\n#include <stdio.h>\n\nstruct Edge;\nstruct Node;\n\nextern bool g_explaining;\n\nextern bool g_keep_depfile;\n\nextern bool g_keep_rsp;\n\nextern bool g_experimental_statcache;\n\n#endif // NINJA_EXPLAIN_H_\n"
  },
  {
    "path": "src/depfile_parser.cc",
    "content": "/* Generated by re2c */\n// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"depfile_parser.h\"\n#include \"util.h\"\n\n#include <algorithm>\n\nusing namespace std;\n\nDepfileParser::DepfileParser(DepfileParserOptions options)\n  : options_(options)\n{\n}\n\n// A note on backslashes in Makefiles, from reading the docs:\n// Backslash-newline is the line continuation character.\n// Backslash-# escapes a # (otherwise meaningful as a comment start).\n// Backslash-% escapes a % (otherwise meaningful as a special).\n// Finally, quoting the GNU manual, \"Backslashes that are not in danger\n// of quoting ‘%’ characters go unmolested.\"\n// How do you end a line with a backslash?  The netbsd Make docs suggest\n// reading the result of a shell command echoing a backslash!\n//\n// Rather than implement all of above, we follow what GCC/Clang produces:\n// Backslashes escape a space or hash sign.\n// When a space is preceded by 2N+1 backslashes, it is represents N backslashes\n// followed by space.\n// When a space is preceded by 2N backslashes, it represents 2N backslashes at\n// the end of a filename.\n// A hash sign is escaped by a single backslash. All other backslashes remain\n// unchanged.\n//\n// If anyone actually has depfiles that rely on the more complicated\n// behavior we can adjust this.\nbool DepfileParser::Parse(string* content, string* err) {\n  // in: current parser input point.\n  // end: end of input.\n  // parsing_targets: whether we are parsing targets or dependencies.\n  char* in = &(*content)[0];\n  char* end = in + content->size();\n  bool have_target = false;\n  bool parsing_targets = true;\n  bool poisoned_input = false;\n  bool is_empty = true;\n  while (in < end) {\n    bool have_newline = false;\n    // out: current output point (typically same as in, but can fall behind\n    // as we de-escape backslashes).\n    char* out = in;\n    // filename: start of the current parsed filename.\n    char* filename = out;\n    for (;;) {\n      // start: beginning of the current parsed span.\n      const char* start = in;\n      char* yymarker = NULL;\n      \n    {\n      unsigned char yych;\n      static const unsigned char yybm[256] = {\n          0,   0,   0,   0,   0,   0,   0,   0,\n          0,   0,   0,   0,   0,   0,   0,   0,\n          0,   0,   0,   0,   0,   0,   0,   0,\n          0,   0,   0,   0,   0,   0,   0,   0,\n          0, 128, 128,   0,   0, 128, 128, 128,\n        128, 128,   0, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128,   0,   0, 128,   0, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128,   0, 128,   0, 128,\n          0, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128,   0, 128, 128,   0,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128,\n        128, 128, 128, 128, 128, 128, 128, 128\n      };\n      yych = *in;\n      if (yybm[0+yych] & 128) goto yy5;\n      if (yych <= '\\r') {\n        if (yych <= '\\t') {\n          if (yych >= 0x01) goto yy1;\n        } else {\n          if (yych <= '\\n') goto yy3;\n          if (yych <= '\\f') goto yy1;\n          goto yy4;\n        }\n      } else {\n        if (yych <= '$') {\n          if (yych <= '#') goto yy1;\n          goto yy7;\n        } else {\n          if (yych <= '>') goto yy1;\n          if (yych <= '\\\\') goto yy8;\n          goto yy1;\n        }\n      }\n      ++in;\n      {\n        break;\n      }\nyy1:\n      ++in;\nyy2:\n      {\n        // For any other character (e.g. whitespace), swallow it here,\n        // allowing the outer logic to loop around again.\n        break;\n      }\nyy3:\n      ++in;\n      {\n        // A newline ends the current file name and the current rule.\n        have_newline = true;\n        break;\n      }\nyy4:\n      yych = *++in;\n      if (yych == '\\n') goto yy3;\n      goto yy2;\nyy5:\n      yych = *++in;\n      if (yybm[0+yych] & 128) goto yy5;\nyy6:\n      {\n        // Got a span of plain text.\n        int len = (int)(in - start);\n        // Need to shift it over if we're overwriting backslashes.\n        if (out < start)\n          memmove(out, start, len);\n        out += len;\n        continue;\n      }\nyy7:\n      yych = *++in;\n      if (yych == '$') goto yy9;\n      goto yy2;\nyy8:\n      yych = *(yymarker = ++in);\n      if (yych <= ' ') {\n        if (yych <= '\\n') {\n          if (yych <= 0x00) goto yy2;\n          if (yych <= '\\t') goto yy10;\n          goto yy11;\n        } else {\n          if (yych == '\\r') goto yy12;\n          if (yych <= 0x1F) goto yy10;\n          goto yy13;\n        }\n      } else {\n        if (yych <= '9') {\n          if (yych == '#') goto yy14;\n          goto yy10;\n        } else {\n          if (yych <= ':') goto yy15;\n          if (yych == '\\\\') goto yy17;\n          goto yy10;\n        }\n      }\nyy9:\n      ++in;\n      {\n        // De-escape dollar character.\n        *out++ = '$';\n        continue;\n      }\nyy10:\n      ++in;\n      goto yy6;\nyy11:\n      ++in;\n      {\n        // A line continuation ends the current file name.\n        break;\n      }\nyy12:\n      yych = *++in;\n      if (yych == '\\n') goto yy11;\n      in = yymarker;\n      goto yy2;\nyy13:\n      ++in;\n      {\n        // 2N+1 backslashes plus space -> N backslashes plus space.\n        int len = (int)(in - start);\n        int n = len / 2 - 1;\n        if (out < start)\n          memset(out, '\\\\', n);\n        out += n;\n        *out++ = ' ';\n        continue;\n      }\nyy14:\n      ++in;\n      {\n        // De-escape hash sign, but preserve other leading backslashes.\n        int len = (int)(in - start);\n        if (len > 2 && out < start)\n          memset(out, '\\\\', len - 2);\n        out += len - 2;\n        *out++ = '#';\n        continue;\n      }\nyy15:\n      yych = *++in;\n      if (yych <= '\\f') {\n        if (yych <= 0x00) goto yy18;\n        if (yych <= 0x08) goto yy16;\n        if (yych <= '\\n') goto yy18;\n      } else {\n        if (yych <= '\\r') goto yy18;\n        if (yych == ' ') goto yy18;\n      }\nyy16:\n      {\n        // De-escape colon sign, but preserve other leading backslashes.\n        // Regular expression uses lookahead to make sure that no whitespace\n        // nor EOF follows. In that case it'd be the : at the end of a target\n        int len = (int)(in - start);\n        if (len > 2 && out < start)\n          memset(out, '\\\\', len - 2);\n        out += len - 2;\n        *out++ = ':';\n        continue;\n      }\nyy17:\n      yych = *++in;\n      if (yych <= ' ') {\n        if (yych <= '\\n') {\n          if (yych <= 0x00) goto yy6;\n          if (yych <= '\\t') goto yy10;\n          goto yy6;\n        } else {\n          if (yych == '\\r') goto yy6;\n          if (yych <= 0x1F) goto yy10;\n          goto yy19;\n        }\n      } else {\n        if (yych <= '9') {\n          if (yych == '#') goto yy14;\n          goto yy10;\n        } else {\n          if (yych <= ':') goto yy15;\n          if (yych == '\\\\') goto yy20;\n          goto yy10;\n        }\n      }\nyy18:\n      ++in;\n      {\n        // Backslash followed by : and whitespace.\n        // It is therefore normal text and not an escaped colon\n        int len = (int)(in - start - 1);\n        // Need to shift it over if we're overwriting backslashes.\n        if (out < start)\n          memmove(out, start, len);\n        out += len;\n        if (*(in - 1) == '\\n')\n          have_newline = true;\n        break;\n      }\nyy19:\n      ++in;\n      {\n        // 2N backslashes plus space -> 2N backslashes, end of filename.\n        int len = (int)(in - start);\n        if (out < start)\n          memset(out, '\\\\', len - 1);\n        out += len - 1;\n        break;\n      }\nyy20:\n      yych = *++in;\n      if (yych <= ' ') {\n        if (yych <= '\\n') {\n          if (yych <= 0x00) goto yy6;\n          if (yych <= '\\t') goto yy10;\n          goto yy6;\n        } else {\n          if (yych == '\\r') goto yy6;\n          if (yych <= 0x1F) goto yy10;\n          goto yy13;\n        }\n      } else {\n        if (yych <= '9') {\n          if (yych == '#') goto yy14;\n          goto yy10;\n        } else {\n          if (yych <= ':') goto yy15;\n          if (yych == '\\\\') goto yy17;\n          goto yy10;\n        }\n      }\n    }\n\n    }\n\n    int len = (int)(out - filename);\n    const bool is_dependency = !parsing_targets;\n    if (len > 0 && filename[len - 1] == ':') {\n      len--;  // Strip off trailing colon, if any.\n      parsing_targets = false;\n      have_target = true;\n    }\n\n    if (len > 0) {\n      is_empty = false;\n      StringPiece piece = StringPiece(filename, len);\n      // If we've seen this as an input before, skip it.\n      std::vector<StringPiece>::iterator pos = std::find(ins_.begin(), ins_.end(), piece);\n      if (pos == ins_.end()) {\n        if (is_dependency) {\n          if (poisoned_input) {\n            *err = \"inputs may not also have inputs\";\n            return false;\n          }\n          // New input.\n          ins_.push_back(piece);\n        } else {\n          // Check for a new output.\n          if (std::find(outs_.begin(), outs_.end(), piece) == outs_.end())\n            outs_.push_back(piece);\n        }\n      } else if (!is_dependency) {\n        // We've passed an input on the left side; reject new inputs.\n        poisoned_input = true;\n      }\n    }\n\n    if (have_newline) {\n      // A newline ends a rule so the next filename will be a new target.\n      parsing_targets = true;\n      poisoned_input = false;\n    }\n  }\n  if (!have_target && !is_empty) {\n    *err = \"expected ':' in depfile\";\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "src/depfile_parser.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_DEPFILE_PARSER_H_\n#define NINJA_DEPFILE_PARSER_H_\n\n#include <string>\n#include <vector>\n\n#include \"string_piece.h\"\n\nstruct DepfileParserOptions {\n  DepfileParserOptions() {}\n};\n\n/// Parser for the dependency information emitted by gcc's -M flags.\nstruct DepfileParser {\n  explicit DepfileParser(DepfileParserOptions options =\n                         DepfileParserOptions());\n\n  /// Parse an input file.  Input must be NUL-terminated.\n  /// Warning: may mutate the content in-place and parsed StringPieces are\n  /// pointers within it.\n  bool Parse(std::string* content, std::string* err);\n\n  std::vector<StringPiece> outs_;\n  std::vector<StringPiece> ins_;\n  DepfileParserOptions options_;\n};\n\n#endif // NINJA_DEPFILE_PARSER_H_\n"
  },
  {
    "path": "src/depfile_parser.in.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"depfile_parser.h\"\n#include \"util.h\"\n\n#include <algorithm>\n\nusing namespace std;\n\nDepfileParser::DepfileParser(DepfileParserOptions options)\n  : options_(options)\n{\n}\n\n// A note on backslashes in Makefiles, from reading the docs:\n// Backslash-newline is the line continuation character.\n// Backslash-# escapes a # (otherwise meaningful as a comment start).\n// Backslash-% escapes a % (otherwise meaningful as a special).\n// Finally, quoting the GNU manual, \"Backslashes that are not in danger\n// of quoting ‘%’ characters go unmolested.\"\n// How do you end a line with a backslash?  The netbsd Make docs suggest\n// reading the result of a shell command echoing a backslash!\n//\n// Rather than implement all of above, we follow what GCC/Clang produces:\n// Backslashes escape a space or hash sign.\n// When a space is preceded by 2N+1 backslashes, it is represents N backslashes\n// followed by space.\n// When a space is preceded by 2N backslashes, it represents 2N backslashes at\n// the end of a filename.\n// A hash sign is escaped by a single backslash. All other backslashes remain\n// unchanged.\n//\n// If anyone actually has depfiles that rely on the more complicated\n// behavior we can adjust this.\nbool DepfileParser::Parse(string* content, string* err) {\n  // in: current parser input point.\n  // end: end of input.\n  // parsing_targets: whether we are parsing targets or dependencies.\n  char* in = &(*content)[0];\n  char* end = in + content->size();\n  bool have_target = false;\n  bool parsing_targets = true;\n  bool poisoned_input = false;\n  bool is_empty = true;\n  while (in < end) {\n    bool have_newline = false;\n    // out: current output point (typically same as in, but can fall behind\n    // as we de-escape backslashes).\n    char* out = in;\n    // filename: start of the current parsed filename.\n    char* filename = out;\n    for (;;) {\n      // start: beginning of the current parsed span.\n      const char* start = in;\n      char* yymarker = NULL;\n      /*!re2c\n      re2c:define:YYCTYPE = \"unsigned char\";\n      re2c:define:YYCURSOR = in;\n      re2c:define:YYLIMIT = end;\n      re2c:define:YYMARKER = yymarker;\n\n      re2c:yyfill:enable = 0;\n\n      re2c:indent:top = 2;\n      re2c:indent:string = \"  \";\n\n      nul = \"\\000\";\n      newline = '\\r'?'\\n';\n\n      '\\\\\\\\'* '\\\\ ' {\n        // 2N+1 backslashes plus space -> N backslashes plus space.\n        int len = (int)(in - start);\n        int n = len / 2 - 1;\n        if (out < start)\n          memset(out, '\\\\', n);\n        out += n;\n        *out++ = ' ';\n        continue;\n      }\n      '\\\\\\\\'+ ' ' {\n        // 2N backslashes plus space -> 2N backslashes, end of filename.\n        int len = (int)(in - start);\n        if (out < start)\n          memset(out, '\\\\', len - 1);\n        out += len - 1;\n        break;\n      }\n      '\\\\'+ '#' {\n        // De-escape hash sign, but preserve other leading backslashes.\n        int len = (int)(in - start);\n        if (len > 2 && out < start)\n          memset(out, '\\\\', len - 2);\n        out += len - 2;\n        *out++ = '#';\n        continue;\n      }\n      '\\\\'+ ':' [\\x00\\x20\\r\\n\\t] {\n        // Backslash followed by : and whitespace.\n        // It is therefore normal text and not an escaped colon\n        int len = (int)(in - start - 1);\n        // Need to shift it over if we're overwriting backslashes.\n        if (out < start)\n          memmove(out, start, len);\n        out += len;\n        if (*(in - 1) == '\\n')\n          have_newline = true;\n        break;\n      }\n      '\\\\'+ ':' {\n        // De-escape colon sign, but preserve other leading backslashes.\n        // Regular expression uses lookahead to make sure that no whitespace\n        // nor EOF follows. In that case it'd be the : at the end of a target\n        int len = (int)(in - start);\n        if (len > 2 && out < start)\n          memset(out, '\\\\', len - 2);\n        out += len - 2;\n        *out++ = ':';\n        continue;\n      }\n      '$$' {\n        // De-escape dollar character.\n        *out++ = '$';\n        continue;\n      }\n      '\\\\'+ [^\\000\\r\\n] | [a-zA-Z0-9+?\"'&,/_:.~()}{%=@\\x5B\\x5D!\\x80-\\xFF-]+ {\n        // Got a span of plain text.\n        int len = (int)(in - start);\n        // Need to shift it over if we're overwriting backslashes.\n        if (out < start)\n          memmove(out, start, len);\n        out += len;\n        continue;\n      }\n      nul {\n        break;\n      }\n      '\\\\' newline {\n        // A line continuation ends the current file name.\n        break;\n      }\n      newline {\n        // A newline ends the current file name and the current rule.\n        have_newline = true;\n        break;\n      }\n      [^] {\n        // For any other character (e.g. whitespace), swallow it here,\n        // allowing the outer logic to loop around again.\n        break;\n      }\n      */\n    }\n\n    int len = (int)(out - filename);\n    const bool is_dependency = !parsing_targets;\n    if (len > 0 && filename[len - 1] == ':') {\n      len--;  // Strip off trailing colon, if any.\n      parsing_targets = false;\n      have_target = true;\n    }\n\n    if (len > 0) {\n      is_empty = false;\n      StringPiece piece = StringPiece(filename, len);\n      // If we've seen this as an input before, skip it.\n      std::vector<StringPiece>::iterator pos = std::find(ins_.begin(), ins_.end(), piece);\n      if (pos == ins_.end()) {\n        if (is_dependency) {\n          if (poisoned_input) {\n            *err = \"inputs may not also have inputs\";\n            return false;\n          }\n          // New input.\n          ins_.push_back(piece);\n        } else {\n          // Check for a new output.\n          if (std::find(outs_.begin(), outs_.end(), piece) == outs_.end())\n            outs_.push_back(piece);\n        }\n      } else if (!is_dependency) {\n        // We've passed an input on the left side; reject new inputs.\n        poisoned_input = true;\n      }\n    }\n\n    if (have_newline) {\n      // A newline ends a rule so the next filename will be a new target.\n      parsing_targets = true;\n      poisoned_input = false;\n    }\n  }\n  if (!have_target && !is_empty) {\n    *err = \"expected ':' in depfile\";\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "src/depfile_parser_perftest.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"depfile_parser.h\"\n#include \"util.h\"\n#include \"metrics.h\"\n\nusing namespace std;\n\nint main(int argc, char* argv[]) {\n  if (argc < 2) {\n    printf(\"usage: %s <file1> <file2...>\\n\", argv[0]);\n    return 1;\n  }\n\n  vector<float> times;\n  for (int i = 1; i < argc; ++i) {\n    const char* filename = argv[i];\n\n    for (int limit = 1 << 10; limit < (1<<20); limit *= 2) {\n      int64_t start = GetTimeMillis();\n      for (int rep = 0; rep < limit; ++rep) {\n        string buf;\n        string err;\n        if (ReadFile(filename, &buf, &err) < 0) {\n          printf(\"%s: %s\\n\", filename, err.c_str());\n          return 1;\n        }\n\n        DepfileParser parser;\n        if (!parser.Parse(&buf, &err)) {\n          printf(\"%s: %s\\n\", filename, err.c_str());\n          return 1;\n        }\n      }\n      int64_t end = GetTimeMillis();\n\n      if (end - start > 100) {\n        int delta = (int)(end - start);\n        float time = delta*1000 / (float)limit;\n        printf(\"%s: %.1fus\\n\", filename, time);\n        times.push_back(time);\n        break;\n      }\n    }\n  }\n\n  if (!times.empty()) {\n    float min = times[0];\n    float max = times[0];\n    float total = 0;\n    for (size_t i = 0; i < times.size(); ++i) {\n      total += times[i];\n      if (times[i] < min)\n        min = times[i];\n      else if (times[i] > max)\n        max = times[i];\n    }\n\n    printf(\"min %.1fus  max %.1fus  avg %.1fus\\n\",\n           min, max, total / times.size());\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "src/depfile_parser_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"depfile_parser.h\"\n\n#include \"test.h\"\n\nusing namespace std;\n\nstruct DepfileParserTest : public testing::Test {\n  bool Parse(const char* input, string* err);\n\n  DepfileParser parser_;\n  string input_;\n};\n\nbool DepfileParserTest::Parse(const char* input, string* err) {\n  input_ = input;\n  return parser_.Parse(&input_, err);\n}\n\nTEST_F(DepfileParserTest, Basic) {\n  string err;\n  EXPECT_TRUE(Parse(\n\"build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h\\n\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"build/ninja.o\", parser_.outs_[0].AsString());\n  EXPECT_EQ(4u, parser_.ins_.size());\n}\n\nTEST_F(DepfileParserTest, EarlyNewlineAndWhitespace) {\n  string err;\n  EXPECT_TRUE(Parse(\n\" \\\\\\n\"\n\"  out: in\\n\",\n      &err));\n  ASSERT_EQ(\"\", err);\n}\n\nTEST_F(DepfileParserTest, Continuation) {\n  string err;\n  EXPECT_TRUE(Parse(\n\"foo.o: \\\\\\n\"\n\"  bar.h baz.h\\n\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"foo.o\", parser_.outs_[0].AsString());\n  EXPECT_EQ(2u, parser_.ins_.size());\n}\n\nTEST_F(DepfileParserTest, WindowsDrivePaths) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo.o: //?/c:/bar.h\\n\", &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"foo.o\", parser_.outs_[0].AsString());\n  EXPECT_EQ(1u, parser_.ins_.size());\n  EXPECT_EQ(\"//?/c:/bar.h\", parser_.ins_[0].AsString());\n}\n\nTEST_F(DepfileParserTest, AmpersandsAndQuotes) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo&bar.o foo'bar.o foo\\\"bar.o: foo&bar.h foo'bar.h foo\\\"bar.h\\n\", &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(3u, parser_.outs_.size());\n  EXPECT_EQ(\"foo&bar.o\", parser_.outs_[0].AsString());\n  EXPECT_EQ(\"foo'bar.o\", parser_.outs_[1].AsString());\n  EXPECT_EQ(\"foo\\\"bar.o\", parser_.outs_[2].AsString());\n  EXPECT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"foo&bar.h\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"foo'bar.h\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"foo\\\"bar.h\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, CarriageReturnContinuation) {\n  string err;\n  EXPECT_TRUE(Parse(\n\"foo.o: \\\\\\r\\n\"\n\"  bar.h baz.h\\r\\n\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"foo.o\", parser_.outs_[0].AsString());\n  EXPECT_EQ(2u, parser_.ins_.size());\n}\n\nTEST_F(DepfileParserTest, BackSlashes) {\n  string err;\n  EXPECT_TRUE(Parse(\n\"Project\\\\Dir\\\\Build\\\\Release8\\\\Foo\\\\Foo.res : \\\\\\n\"\n\"  Dir\\\\Library\\\\Foo.rc \\\\\\n\"\n\"  Dir\\\\Library\\\\Version\\\\Bar.h \\\\\\n\"\n\"  Dir\\\\Library\\\\Foo.ico \\\\\\n\"\n\"  Project\\\\Thing\\\\Bar.tlb \\\\\\n\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"Project\\\\Dir\\\\Build\\\\Release8\\\\Foo\\\\Foo.res\",\n            parser_.outs_[0].AsString());\n  EXPECT_EQ(4u, parser_.ins_.size());\n}\n\nTEST_F(DepfileParserTest, Spaces) {\n  string err;\n  EXPECT_TRUE(Parse(\n\"a\\\\ bc\\\\ def:   a\\\\ b c d\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"a bc def\",\n            parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"a b\",\n            parser_.ins_[0].AsString());\n  EXPECT_EQ(\"c\",\n            parser_.ins_[1].AsString());\n  EXPECT_EQ(\"d\",\n            parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, MultipleBackslashes) {\n  // Successive 2N+1 backslashes followed by space (' ') are replaced by N >= 0\n  // backslashes and the space. A single backslash before hash sign is removed.\n  // Other backslashes remain untouched (including 2N backslashes followed by\n  // space).\n  string err;\n  EXPECT_TRUE(Parse(\n\"a\\\\ b\\\\#c.h: \\\\\\\\\\\\\\\\\\\\  \\\\\\\\\\\\\\\\ \\\\\\\\share\\\\info\\\\\\\\#1\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"a b#c.h\",\n            parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"\\\\\\\\ \",\n            parser_.ins_[0].AsString());\n  EXPECT_EQ(\"\\\\\\\\\\\\\\\\\",\n            parser_.ins_[1].AsString());\n  EXPECT_EQ(\"\\\\\\\\share\\\\info\\\\#1\",\n            parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, Escapes) {\n  // Put backslashes before a variety of characters, see which ones make\n  // it through.\n  string err;\n  EXPECT_TRUE(Parse(\n\"\\\\!\\\\@\\\\#$$\\\\%\\\\^\\\\&\\\\[\\\\]\\\\\\\\:\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"\\\\!\\\\@#$\\\\%\\\\^\\\\&\\\\[\\\\]\\\\\\\\\",\n            parser_.outs_[0].AsString());\n  ASSERT_EQ(0u, parser_.ins_.size());\n}\n\nTEST_F(DepfileParserTest, EscapedColons)\n{\n  std::string err;\n  // Tests for correct parsing of depfiles produced on Windows\n  // by both Clang, GCC pre 10 and GCC 10\n  EXPECT_TRUE(Parse(\n\"c\\\\:\\\\gcc\\\\x86_64-w64-mingw32\\\\include\\\\stddef.o: \\\\\\n\"\n\" c:\\\\gcc\\\\x86_64-w64-mingw32\\\\include\\\\stddef.h \\n\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"c:\\\\gcc\\\\x86_64-w64-mingw32\\\\include\\\\stddef.o\",\n            parser_.outs_[0].AsString());\n  ASSERT_EQ(1u, parser_.ins_.size());\n  EXPECT_EQ(\"c:\\\\gcc\\\\x86_64-w64-mingw32\\\\include\\\\stddef.h\",\n            parser_.ins_[0].AsString());\n}\n\nTEST_F(DepfileParserTest, EscapedTargetColon)\n{\n  std::string err;\n  EXPECT_TRUE(Parse(\n\"foo1\\\\: x\\n\"\n\"foo1\\\\:\\n\"\n\"foo1\\\\:\\r\\n\"\n\"foo1\\\\:\\t\\n\"\n\"foo1\\\\:\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"foo1\\\\\", parser_.outs_[0].AsString());\n  ASSERT_EQ(1u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n}\n\nTEST_F(DepfileParserTest, SpecialChars) {\n  // See filenames like istreambuf.iterator_op!= in\n  // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/\n  string err;\n  EXPECT_TRUE(Parse(\n\"C:/Program\\\\ Files\\\\ (x86)/Microsoft\\\\ crtdefs.h: \\\\\\n\"\n\" en@quot.header~ t+t-x!=1 \\\\\\n\"\n\" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\\\\\\n\"\n\" Fu\\303\\244ball\\\\\\n\"\n\" a[1]b@2%c\",\n      &err));\n  ASSERT_EQ(\"\", err);\n  ASSERT_EQ(1u, parser_.outs_.size());\n  EXPECT_EQ(\"C:/Program Files (x86)/Microsoft crtdefs.h\",\n            parser_.outs_[0].AsString());\n  ASSERT_EQ(5u, parser_.ins_.size());\n  EXPECT_EQ(\"en@quot.header~\",\n            parser_.ins_[0].AsString());\n  EXPECT_EQ(\"t+t-x!=1\",\n            parser_.ins_[1].AsString());\n  EXPECT_EQ(\"openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\",\n            parser_.ins_[2].AsString());\n  EXPECT_EQ(\"Fu\\303\\244ball\",\n            parser_.ins_[3].AsString());\n  EXPECT_EQ(\"a[1]b@2%c\",\n            parser_.ins_[4].AsString());\n}\n\nTEST_F(DepfileParserTest, UnifyMultipleOutputs) {\n  // check that multiple duplicate targets are properly unified\n  string err;\n  EXPECT_TRUE(Parse(\"foo foo: x y z\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, MultipleDifferentOutputs) {\n  // check that multiple different outputs are accepted by the parser\n  string err;\n  EXPECT_TRUE(Parse(\"foo bar: x y z\", &err));\n  ASSERT_EQ(2u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(\"bar\", parser_.outs_[1].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, MultipleEmptyRules) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x\\n\"\n                    \"foo: \\n\"\n                    \"foo:\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(1u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n}\n\nTEST_F(DepfileParserTest, UnifyMultipleRulesLF) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x\\n\"\n                    \"foo: y\\n\"\n                    \"foo \\\\\\n\"\n                    \"foo: z\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, UnifyMultipleRulesCRLF) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x\\r\\n\"\n                    \"foo: y\\r\\n\"\n                    \"foo \\\\\\r\\n\"\n                    \"foo: z\\r\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, UnifyMixedRulesLF) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x\\\\\\n\"\n                    \"     y\\n\"\n                    \"foo \\\\\\n\"\n                    \"foo: z\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, UnifyMixedRulesCRLF) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x\\\\\\r\\n\"\n                    \"     y\\r\\n\"\n                    \"foo \\\\\\r\\n\"\n                    \"foo: z\\r\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, IndentedRulesLF) {\n  string err;\n  EXPECT_TRUE(Parse(\" foo: x\\n\"\n                    \" foo: y\\n\"\n                    \" foo: z\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, IndentedRulesCRLF) {\n  string err;\n  EXPECT_TRUE(Parse(\" foo: x\\r\\n\"\n                    \" foo: y\\r\\n\"\n                    \" foo: z\\r\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, TolerateMP) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x y z\\n\"\n                    \"x:\\n\"\n                    \"y:\\n\"\n                    \"z:\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, MultipleRulesTolerateMP) {\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x\\n\"\n                    \"x:\\n\"\n                    \"foo: y\\n\"\n                    \"y:\\n\"\n                    \"foo: z\\n\"\n                    \"z:\\n\", &err));\n  ASSERT_EQ(1u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, MultipleRulesDifferentOutputs) {\n  // check that multiple different outputs are accepted by the parser\n  // when spread across multiple rules\n  string err;\n  EXPECT_TRUE(Parse(\"foo: x y\\n\"\n                    \"bar: y z\\n\", &err));\n  ASSERT_EQ(2u, parser_.outs_.size());\n  ASSERT_EQ(\"foo\", parser_.outs_[0].AsString());\n  ASSERT_EQ(\"bar\", parser_.outs_[1].AsString());\n  ASSERT_EQ(3u, parser_.ins_.size());\n  EXPECT_EQ(\"x\", parser_.ins_[0].AsString());\n  EXPECT_EQ(\"y\", parser_.ins_[1].AsString());\n  EXPECT_EQ(\"z\", parser_.ins_[2].AsString());\n}\n\nTEST_F(DepfileParserTest, BuggyMP) {\n  std::string err;\n  EXPECT_FALSE(Parse(\"foo: x y z\\n\"\n                     \"x: alsoin\\n\"\n                     \"y:\\n\"\n                     \"z:\\n\", &err));\n  ASSERT_EQ(\"inputs may not also have inputs\", err);\n}\n\nTEST_F(DepfileParserTest, EmptyFile) {\n  std::string err;\n  EXPECT_TRUE(Parse(\"\", &err));\n  ASSERT_EQ(0u, parser_.outs_.size());\n  ASSERT_EQ(0u, parser_.ins_.size());\n}\n\nTEST_F(DepfileParserTest, EmptyLines) {\n  std::string err;\n  EXPECT_TRUE(Parse(\"\\n\\n\", &err));\n  ASSERT_EQ(0u, parser_.outs_.size());\n  ASSERT_EQ(0u, parser_.ins_.size());\n}\n\nTEST_F(DepfileParserTest, MissingColon) {\n  // The file is not empty but is missing a colon separator.\n  std::string err;\n  EXPECT_FALSE(Parse(\"foo.o foo.c\\n\", &err));\n  EXPECT_EQ(\"expected ':' in depfile\", err);\n}\n"
  },
  {
    "path": "src/deps_log.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"deps_log.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <string.h>\n#ifndef _WIN32\n#include <unistd.h>\n#elif defined(_MSC_VER) && (_MSC_VER < 1900)\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\n#endif\n\n#include \"graph.h\"\n#include \"metrics.h\"\n#include \"state.h\"\n#include \"util.h\"\n\nusing namespace std;\n\n// The version is stored as 4 bytes after the signature and also serves as a\n// byte order mark. Signature and version combined are 16 bytes long.\nstatic const char kFileSignature[] = \"# ninjadeps\\n\";\nstatic const size_t kFileSignatureSize = sizeof(kFileSignature) - 1u;\n\nstatic const int32_t kCurrentVersion = 4;\n\n// Record size is currently limited to less than the full 32 bit, due to\n// internal buffers having to have this size.\nstatic constexpr size_t kMaxRecordSize = (1 << 19) - 1;\n\nDepsLog::~DepsLog() {\n  Close();\n}\n\nbool DepsLog::OpenForWrite(const string& path, string* err) {\n  if (needs_recompaction_) {\n    if (!Recompact(path, err))\n      return false;\n  }\n\n  assert(!file_);\n  file_path_ = path;  // we don't actually open the file right now, but will do\n                      // so on the first write attempt\n  return true;\n}\n\nbool DepsLog::RecordDeps(Node* node, TimeStamp mtime,\n                         const vector<Node*>& nodes) {\n  return RecordDeps(node, mtime, static_cast<int>(nodes.size()), nodes.data());\n}\n\nbool DepsLog::RecordDeps(Node* node, TimeStamp mtime, int node_count,\n                         Node* const* nodes) {\n  // Track whether there's any new data to be recorded.\n  bool made_change = false;\n\n  // Assign ids to all nodes that are missing one.\n  if (node->id() < 0) {\n    if (!RecordId(node))\n      return false;\n    made_change = true;\n  }\n  for (int i = 0; i < node_count; ++i) {\n    if (nodes[i]->id() < 0) {\n      if (!RecordId(nodes[i]))\n        return false;\n      made_change = true;\n    }\n  }\n\n  // See if the new data is different than the existing data, if any.\n  if (!made_change) {\n    Deps* deps = GetDeps(node);\n    if (!deps ||\n        deps->mtime != mtime ||\n        deps->node_count != node_count) {\n      made_change = true;\n    } else {\n      for (int i = 0; i < node_count; ++i) {\n        if (deps->nodes[i] != nodes[i]) {\n          made_change = true;\n          break;\n        }\n      }\n    }\n  }\n\n  // Don't write anything if there's no new info.\n  if (!made_change)\n    return true;\n\n  // Update on-disk representation.\n  unsigned size = 4 * (1 + 2 + node_count);\n  if (size > kMaxRecordSize) {\n    errno = ERANGE;\n    return false;\n  }\n\n  if (!OpenForWriteIfNeeded()) {\n    return false;\n  }\n  size |= 0x80000000;  // Deps record: set high bit.\n  if (fwrite(&size, 4, 1, file_) < 1)\n    return false;\n  int id = node->id();\n  if (fwrite(&id, 4, 1, file_) < 1)\n    return false;\n  uint32_t mtime_part = static_cast<uint32_t>(mtime & 0xffffffff);\n  if (fwrite(&mtime_part, 4, 1, file_) < 1)\n    return false;\n  mtime_part = static_cast<uint32_t>((mtime >> 32) & 0xffffffff);\n  if (fwrite(&mtime_part, 4, 1, file_) < 1)\n    return false;\n  for (int i = 0; i < node_count; ++i) {\n    id = nodes[i]->id();\n    if (fwrite(&id, 4, 1, file_) < 1)\n      return false;\n  }\n  if (fflush(file_) != 0)\n    return false;\n\n  // Update in-memory representation.\n  Deps* deps = new Deps(mtime, node_count);\n  for (int i = 0; i < node_count; ++i)\n    deps->nodes[i] = nodes[i];\n  UpdateDeps(node->id(), deps);\n\n  return true;\n}\n\nvoid DepsLog::Close() {\n  OpenForWriteIfNeeded();  // create the file even if nothing has been recorded\n  if (file_)\n    fclose(file_);\n  file_ = NULL;\n}\n\nLoadStatus DepsLog::Load(const string& path, State* state, string* err) {\n  METRIC_RECORD(\".ninja_deps load\");\n  char buf[kMaxRecordSize + 1];\n  FILE* f = fopen(path.c_str(), \"rb\");\n  if (!f) {\n    if (errno == ENOENT)\n      return LOAD_NOT_FOUND;\n    *err = strerror(errno);\n    return LOAD_ERROR;\n  }\n\n  bool valid_header = fread(buf, kFileSignatureSize, 1, f) == 1 &&\n                      !memcmp(buf, kFileSignature, kFileSignatureSize);\n\n  int32_t version = 0;\n  bool valid_version =\n      fread(&version, 4, 1, f) == 1 && version == kCurrentVersion;\n\n  // Note: For version differences, this should migrate to the new format.\n  // But the v1 format could sometimes (rarely) end up with invalid data, so\n  // don't migrate v1 to v3 to force a rebuild. (v2 only existed for a few days,\n  // and there was no release with it, so pretend that it never happened.)\n  if (!valid_header || !valid_version) {\n    if (version == 1)\n      *err = \"deps log version change; rebuilding\";\n    else\n      *err = \"bad deps log signature or version; starting over\";\n    fclose(f);\n    platformAwareUnlink(path.c_str());\n    // Don't report this as a failure.  An empty deps log will cause\n    // us to rebuild the outputs anyway.\n    return LOAD_SUCCESS;\n  }\n\n  long offset = ftell(f);\n  bool read_failed = false;\n  int unique_dep_record_count = 0;\n  int total_dep_record_count = 0;\n  for (;;) {\n    unsigned size;\n    if (fread(&size, sizeof(size), 1, f) < 1) {\n      if (!feof(f))\n        read_failed = true;\n      break;\n    }\n    bool is_deps = (size >> 31) != 0;\n    size = size & 0x7FFFFFFF;\n\n    if (size > kMaxRecordSize || fread(buf, size, 1, f) < 1) {\n      read_failed = true;\n      break;\n    }\n    offset += size + sizeof(size);\n\n    if (is_deps) {\n      if ((size % 4) != 0) {\n        read_failed = true;\n        break;\n      }\n      int* deps_data = reinterpret_cast<int*>(buf);\n      int out_id = deps_data[0];\n      TimeStamp mtime;\n      mtime = (TimeStamp)(((uint64_t)(unsigned int)deps_data[2] << 32) |\n                          (uint64_t)(unsigned int)deps_data[1]);\n      deps_data += 3;\n      int deps_count = (size / 4) - 3;\n\n      for (int i = 0; i < deps_count; ++i) {\n        int node_id = deps_data[i];\n        if (node_id >= (int)nodes_.size() || !nodes_[node_id]) {\n          read_failed = true;\n          break;\n        }\n      }\n      if (read_failed)\n        break;\n\n      Deps* deps = new Deps(mtime, deps_count);\n      for (int i = 0; i < deps_count; ++i) {\n        deps->nodes[i] = nodes_[deps_data[i]];\n      }\n\n      total_dep_record_count++;\n      if (!UpdateDeps(out_id, deps))\n        ++unique_dep_record_count;\n    } else {\n      int path_size = size - 4;\n      if (path_size <= 0) {\n        read_failed = true;\n        break;\n      }\n      // There can be up to 3 bytes of padding.\n      if (buf[path_size - 1] == '\\0') --path_size;\n      if (buf[path_size - 1] == '\\0') --path_size;\n      if (buf[path_size - 1] == '\\0') --path_size;\n      StringPiece subpath(buf, path_size);\n      // It is not necessary to pass in a correct slash_bits here. It will\n      // either be a Node that's in the manifest (in which case it will already\n      // have a correct slash_bits that GetNode will look up), or it is an\n      // implicit dependency from a .d which does not affect the build command\n      // (and so need not have its slashes maintained).\n      Node* node = state->GetNode(subpath, 0);\n\n      // Check that the expected index matches the actual index. This can only\n      // happen if two ninja processes write to the same deps log concurrently.\n      // (This uses unary complement to make the checksum look less like a\n      // dependency record entry.)\n      unsigned checksum = *reinterpret_cast<unsigned*>(buf + size - 4);\n      int expected_id = ~checksum;\n      int id = static_cast<int>(nodes_.size());\n      if (id != expected_id || node->id() >= 0) {\n        read_failed = true;\n        break;\n      }\n      node->set_id(id);\n      nodes_.push_back(node);\n    }\n  }\n\n  if (read_failed) {\n    // An error occurred while loading; try to recover by truncating the\n    // file to the last fully-read record.\n    if (ferror(f)) {\n      *err = strerror(ferror(f));\n    } else {\n      *err = \"premature end of file\";\n    }\n    fclose(f);\n\n    if (!Truncate(path, offset, err))\n      return LOAD_ERROR;\n\n    // The truncate succeeded; we'll just report the load error as a\n    // warning because the build can proceed.\n    *err += \"; recovering\";\n    return LOAD_SUCCESS;\n  }\n\n  fclose(f);\n\n  // Rebuild the log if there are too many dead records.\n  int kMinCompactionEntryCount = 1000;\n  int kCompactionRatio = 3;\n  if (total_dep_record_count > kMinCompactionEntryCount &&\n      total_dep_record_count > unique_dep_record_count * kCompactionRatio) {\n    needs_recompaction_ = true;\n  }\n\n  return LOAD_SUCCESS;\n}\n\nDepsLog::Deps* DepsLog::GetDeps(Node* node) {\n  // Abort if the node has no id (never referenced in the deps) or if\n  // there's no deps recorded for the node.\n  if (node->id() < 0 || node->id() >= (int)deps_.size())\n    return NULL;\n  return deps_[node->id()];\n}\n\nNode* DepsLog::GetFirstReverseDepsNode(Node* node) {\n  for (size_t id = 0; id < deps_.size(); ++id) {\n    Deps* deps = deps_[id];\n    if (!deps)\n      continue;\n    for (int i = 0; i < deps->node_count; ++i) {\n      if (deps->nodes[i] == node)\n        return nodes_[id];\n    }\n  }\n  return NULL;\n}\n\nbool DepsLog::Recompact(const string& path, string* err) {\n  METRIC_RECORD(\".ninja_deps recompact\");\n\n  Close();\n  string temp_path = path + \".recompact\";\n\n  // OpenForWrite() opens for append.  Make sure it's not appending to a\n  // left-over file from a previous recompaction attempt that crashed somehow.\n  platformAwareUnlink(temp_path.c_str());\n\n  DepsLog new_log;\n  if (!new_log.OpenForWrite(temp_path, err))\n    return false;\n\n  // Clear all known ids so that new ones can be reassigned.  The new indices\n  // will refer to the ordering in new_log, not in the current log.\n  for (vector<Node*>::iterator i = nodes_.begin(); i != nodes_.end(); ++i)\n    (*i)->set_id(-1);\n\n  // Write out all deps again.\n  for (int old_id = 0; old_id < (int)deps_.size(); ++old_id) {\n    Deps* deps = deps_[old_id];\n    if (!deps) continue;  // If nodes_[old_id] is a leaf, it has no deps.\n\n    if (!IsDepsEntryLiveFor(nodes_[old_id]))\n      continue;\n\n    if (!new_log.RecordDeps(nodes_[old_id], deps->mtime,\n                            deps->node_count, deps->nodes)) {\n      new_log.Close();\n      return false;\n    }\n  }\n\n  new_log.Close();\n\n  // All nodes now have ids that refer to new_log, so steal its data.\n  deps_.swap(new_log.deps_);\n  nodes_.swap(new_log.nodes_);\n\n  return ReplaceContent(path, temp_path, err);\n}\n\nbool DepsLog::IsDepsEntryLiveFor(const Node* node) {\n  // Skip entries that don't have in-edges or whose edges don't have a\n  // \"deps\" attribute. They were in the deps log from previous builds, but\n  // the the files they were for were removed from the build and their deps\n  // entries are no longer needed.\n  // (Without the check for \"deps\", a chain of two or more nodes that each\n  // had deps wouldn't be collected in a single recompaction.)\n  return node->in_edge() && !node->in_edge()->GetBinding(\"deps\").empty();\n}\n\nbool DepsLog::UpdateDeps(int out_id, Deps* deps) {\n  if (out_id >= (int)deps_.size())\n    deps_.resize(out_id + 1);\n\n  bool delete_old = deps_[out_id] != NULL;\n  if (delete_old)\n    delete deps_[out_id];\n  deps_[out_id] = deps;\n  return delete_old;\n}\n\nbool DepsLog::RecordId(Node* node) {\n  int path_size = static_cast<int>(node->path().size());\n  assert(path_size > 0 && \"Trying to record empty path Node!\");\n  int padding = (4 - path_size % 4) % 4;  // Pad path to 4 byte boundary.\n\n  unsigned size = path_size + padding + 4;\n  if (size > kMaxRecordSize) {\n    errno = ERANGE;\n    return false;\n  }\n\n  if (!OpenForWriteIfNeeded()) {\n    return false;\n  }\n  if (fwrite(&size, 4, 1, file_) < 1)\n    return false;\n  if (fwrite(node->path().data(), path_size, 1, file_) < 1) {\n    return false;\n  }\n  if (padding && fwrite(\"\\0\\0\", padding, 1, file_) < 1)\n    return false;\n  int id = static_cast<int>(nodes_.size());\n  unsigned checksum = ~(unsigned)id;\n  if (fwrite(&checksum, 4, 1, file_) < 1)\n    return false;\n  if (fflush(file_) != 0)\n    return false;\n\n  node->set_id(id);\n  nodes_.push_back(node);\n\n  return true;\n}\n\nbool DepsLog::OpenForWriteIfNeeded() {\n  if (file_path_.empty()) {\n    return true;\n  }\n  file_ = fopen(file_path_.c_str(), \"ab\");\n  if (!file_) {\n    return false;\n  }\n  // Set the buffer size to this and flush the file buffer after every record\n  // to make sure records aren't written partially.\n  if (setvbuf(file_, NULL, _IOFBF, kMaxRecordSize + 1) != 0) {\n    return false;\n  }\n  SetCloseOnExec(fileno(file_));\n\n  // Opening a file in append mode doesn't set the file pointer to the file's\n  // end on Windows. Do that explicitly.\n  fseek(file_, 0, SEEK_END);\n\n  if (ftell(file_) == 0) {\n    if (fwrite(kFileSignature, sizeof(kFileSignature) - 1, 1, file_) < 1) {\n      return false;\n    }\n    if (fwrite(&kCurrentVersion, 4, 1, file_) < 1) {\n      return false;\n    }\n  }\n  if (fflush(file_) != 0) {\n    return false;\n  }\n  file_path_.clear();\n  return true;\n}\n"
  },
  {
    "path": "src/deps_log.h",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_DEPS_LOG_H_\n#define NINJA_DEPS_LOG_H_\n\n#include <string>\n#include <vector>\n\n#include <stdio.h>\n\n#include \"load_status.h\"\n#include \"timestamp.h\"\n\nstruct Node;\nstruct State;\n\n/// As build commands run they can output extra dependency information\n/// (e.g. header dependencies for C source) dynamically.  DepsLog collects\n/// that information at build time and uses it for subsequent builds.\n///\n/// The on-disk format is based on two primary design constraints:\n/// - it must be written to as a stream (during the build, which may be\n///   interrupted);\n/// - it can be read all at once on startup.  (Alternative designs, where\n///   it contains indexing information, were considered and discarded as\n///   too complicated to implement; if the file is small than reading it\n///   fully on startup is acceptable.)\n/// Here are some stats from the Windows Chrome dependency files, to\n/// help guide the design space.  The total text in the files sums to\n/// 90mb so some compression is warranted to keep load-time fast.\n/// There's about 10k files worth of dependencies that reference about\n/// 40k total paths totalling 2mb of unique strings.\n///\n/// Based on these stats, here's the current design.\n/// The file is structured as version header followed by a sequence of records.\n/// Each record is either a path string or a dependency list.\n/// Numbering the path strings in file order gives them dense integer ids.\n/// A dependency list maps an output id to a list of input ids.\n///\n/// Concretely, a record is:\n///    four bytes record length, high bit indicates record type\n///      (but max record sizes are capped at 512kB)\n///    path records contain the string name of the path, followed by up to 3\n///      padding bytes to align on 4 byte boundaries, followed by the\n///      one's complement of the expected index of the record (to detect\n///      concurrent writes of multiple ninja processes to the log).\n///    dependency records are an array of 4-byte integers\n///      [output path id,\n///       output path mtime (lower 4 bytes), output path mtime (upper 4 bytes),\n///       input path id, input path id...]\n///      (The mtime is compared against the on-disk output path mtime\n///      to verify the stored data is up-to-date.)\n/// If two records reference the same output the latter one in the file\n/// wins, allowing updates to just be appended to the file.  A separate\n/// repacking step can run occasionally to remove dead records.\nstruct DepsLog {\n  DepsLog() : needs_recompaction_(false), file_(NULL) {}\n  ~DepsLog();\n\n  // Writing (build-time) interface.\n  bool OpenForWrite(const std::string& path, std::string* err);\n  bool RecordDeps(Node* node, TimeStamp mtime, const std::vector<Node*>& nodes);\n  bool RecordDeps(Node* node, TimeStamp mtime, int node_count,\n                  Node* const* nodes);\n  void Close();\n\n  // Reading (startup-time) interface.\n  struct Deps {\n    Deps(int64_t mtime, int node_count)\n        : mtime(mtime), node_count(node_count), nodes(new Node*[node_count]) {}\n    ~Deps() { delete [] nodes; }\n    TimeStamp mtime;\n    int node_count;\n    Node** nodes;\n  };\n  LoadStatus Load(const std::string& path, State* state, std::string* err);\n  Deps* GetDeps(Node* node);\n  Node* GetFirstReverseDepsNode(Node* node);\n\n  /// Rewrite the known log entries, throwing away old data.\n  bool Recompact(const std::string& path, std::string* err);\n\n  /// Returns if the deps entry for a node is still reachable from the manifest.\n  ///\n  /// The deps log can contain deps entries for files that were built in the\n  /// past but are no longer part of the manifest.  This function returns if\n  /// this is the case for a given node.  This function is slow, don't call\n  /// it from code that runs on every build.\n  static bool IsDepsEntryLiveFor(const Node* node);\n\n  /// Used for tests.\n  const std::vector<Node*>& nodes() const { return nodes_; }\n  const std::vector<Deps*>& deps() const { return deps_; }\n\n private:\n  // Updates the in-memory representation.  Takes ownership of |deps|.\n  // Returns true if a prior deps record was deleted.\n  bool UpdateDeps(int out_id, Deps* deps);\n  // Write a node name record, assigning it an id.\n  bool RecordId(Node* node);\n\n  /// Should be called before using file_. When false is returned, errno will\n  /// be set.\n  bool OpenForWriteIfNeeded();\n\n  bool needs_recompaction_;\n  FILE* file_;\n  std::string file_path_;\n\n  /// Maps id -> Node.\n  std::vector<Node*> nodes_;\n  /// Maps id -> deps of that id.\n  std::vector<Deps*> deps_;\n\n  friend struct DepsLogTest;\n};\n\n#endif  // NINJA_DEPS_LOG_H_\n"
  },
  {
    "path": "src/deps_log_test.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"deps_log.h\"\n\n#include <sys/stat.h>\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\n#include \"disk_interface.h\"\n#include \"graph.h\"\n#include \"util.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nnamespace {\n\nconst char kTestFilename[] = \"DepsLogTest-tempfile\";\n\nstruct DepsLogTest : public testing::Test {\n  virtual void SetUp() {\n    // In case a crashing test left a stale file behind.\n    platformAwareUnlink(kTestFilename);\n  }\n  virtual void TearDown() { platformAwareUnlink(kTestFilename); }\n};\n\nTEST_F(DepsLogTest, WriteRead) {\n  State state1;\n  DepsLog log1;\n  string err;\n  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  {\n    vector<Node*> deps;\n    deps.push_back(state1.GetNode(\"foo.h\", 0));\n    deps.push_back(state1.GetNode(\"bar.h\", 0));\n    log1.RecordDeps(state1.GetNode(\"out.o\", 0), 1, deps);\n\n    deps.clear();\n    deps.push_back(state1.GetNode(\"foo.h\", 0));\n    deps.push_back(state1.GetNode(\"bar2.h\", 0));\n    log1.RecordDeps(state1.GetNode(\"out2.o\", 0), 2, deps);\n\n    DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode(\"out.o\", 0));\n    ASSERT_TRUE(log_deps);\n    ASSERT_EQ(1, log_deps->mtime);\n    ASSERT_EQ(2, log_deps->node_count);\n    ASSERT_EQ(\"foo.h\", log_deps->nodes[0]->path());\n    ASSERT_EQ(\"bar.h\", log_deps->nodes[1]->path());\n  }\n\n  log1.Close();\n\n  State state2;\n  DepsLog log2;\n  EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err));\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_EQ(log1.nodes().size(), log2.nodes().size());\n  for (int i = 0; i < (int)log1.nodes().size(); ++i) {\n    Node* node1 = log1.nodes()[i];\n    Node* node2 = log2.nodes()[i];\n    ASSERT_EQ(i, node1->id());\n    ASSERT_EQ(node1->id(), node2->id());\n  }\n\n  // Spot-check the entries in log2.\n  DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode(\"out2.o\", 0));\n  ASSERT_TRUE(log_deps);\n  ASSERT_EQ(2, log_deps->mtime);\n  ASSERT_EQ(2, log_deps->node_count);\n  ASSERT_EQ(\"foo.h\", log_deps->nodes[0]->path());\n  ASSERT_EQ(\"bar2.h\", log_deps->nodes[1]->path());\n}\n\nTEST_F(DepsLogTest, LotsOfDeps) {\n  const int kNumDeps = 100000;  // More than 64k.\n\n  State state1;\n  DepsLog log1;\n  string err;\n  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  {\n    vector<Node*> deps;\n    for (int i = 0; i < kNumDeps; ++i) {\n      char buf[32];\n      sprintf(buf, \"file%d.h\", i);\n      deps.push_back(state1.GetNode(buf, 0));\n    }\n    log1.RecordDeps(state1.GetNode(\"out.o\", 0), 1, deps);\n\n    DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode(\"out.o\", 0));\n    ASSERT_EQ(kNumDeps, log_deps->node_count);\n  }\n\n  log1.Close();\n\n  State state2;\n  DepsLog log2;\n  EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err));\n  ASSERT_EQ(\"\", err);\n\n  DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode(\"out.o\", 0));\n  ASSERT_EQ(kNumDeps, log_deps->node_count);\n}\n\n// Verify that adding the same deps twice doesn't grow the file.\nTEST_F(DepsLogTest, DoubleEntry) {\n  // Write some deps to the file and grab its size.\n  int file_size;\n  {\n    State state;\n    DepsLog log;\n    string err;\n    EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar.h\", 0));\n    log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n    log.Close();\n#ifdef __USE_LARGEFILE64\n    struct stat64 st;\n    ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n    struct stat st;\n    ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n    file_size = (int)st.st_size;\n    ASSERT_GT(file_size, 0);\n  }\n\n  // Now reload the file, and read the same deps.\n  {\n    State state;\n    DepsLog log;\n    string err;\n    EXPECT_TRUE(log.Load(kTestFilename, &state, &err));\n\n    EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar.h\", 0));\n    log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n    log.Close();\n#ifdef __USE_LARGEFILE64\n    struct stat64 st;\n    ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n    struct stat st;\n    ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n    int file_size_2 = (int)st.st_size;\n    ASSERT_EQ(file_size, file_size_2);\n  }\n}\n\n// Verify that adding the new deps works and can be compacted away.\nTEST_F(DepsLogTest, Recompact) {\n  const char kManifest[] =\n\"rule cc\\n\"\n\"  command = cc\\n\"\n\"  deps = gcc\\n\"\n\"build out.o: cc\\n\"\n\"build other_out.o: cc\\n\";\n\n  // Write some deps to the file and grab its size.\n  int file_size;\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));\n    DepsLog log;\n    string err;\n    ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar.h\", 0));\n    log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n\n    deps.clear();\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"baz.h\", 0));\n    log.RecordDeps(state.GetNode(\"other_out.o\", 0), 1, deps);\n\n    log.Close();\n#ifdef __USE_LARGEFILE64\n    struct stat64 st;\n    ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n    struct stat st;\n    ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n    file_size = (int)st.st_size;\n    ASSERT_GT(file_size, 0);\n  }\n\n  // Now reload the file, and add slightly different deps.\n  int file_size_2;\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));\n    DepsLog log;\n    string err;\n    ASSERT_TRUE(log.Load(kTestFilename, &state, &err));\n\n    ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n    log.Close();\n\n#ifdef __USE_LARGEFILE64\n    struct stat64 st;\n    ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n    struct stat st;\n    ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n    file_size_2 = (int)st.st_size;\n    // The file should grow to record the new deps.\n    ASSERT_GT(file_size_2, file_size);\n  }\n\n  // Now reload the file, verify the new deps have replaced the old, then\n  // recompact.\n  int file_size_3;\n  {\n    State state;\n    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));\n    DepsLog log;\n    string err;\n    ASSERT_TRUE(log.Load(kTestFilename, &state, &err));\n\n    Node* out = state.GetNode(\"out.o\", 0);\n    DepsLog::Deps* deps = log.GetDeps(out);\n    ASSERT_TRUE(deps);\n    ASSERT_EQ(1, deps->mtime);\n    ASSERT_EQ(1, deps->node_count);\n    ASSERT_EQ(\"foo.h\", deps->nodes[0]->path());\n\n    Node* other_out = state.GetNode(\"other_out.o\", 0);\n    deps = log.GetDeps(other_out);\n    ASSERT_TRUE(deps);\n    ASSERT_EQ(1, deps->mtime);\n    ASSERT_EQ(2, deps->node_count);\n    ASSERT_EQ(\"foo.h\", deps->nodes[0]->path());\n    ASSERT_EQ(\"baz.h\", deps->nodes[1]->path());\n\n    ASSERT_TRUE(log.Recompact(kTestFilename, &err));\n\n    // The in-memory deps graph should still be valid after recompaction.\n    deps = log.GetDeps(out);\n    ASSERT_TRUE(deps);\n    ASSERT_EQ(1, deps->mtime);\n    ASSERT_EQ(1, deps->node_count);\n    ASSERT_EQ(\"foo.h\", deps->nodes[0]->path());\n    ASSERT_EQ(out, log.nodes()[out->id()]);\n\n    deps = log.GetDeps(other_out);\n    ASSERT_TRUE(deps);\n    ASSERT_EQ(1, deps->mtime);\n    ASSERT_EQ(2, deps->node_count);\n    ASSERT_EQ(\"foo.h\", deps->nodes[0]->path());\n    ASSERT_EQ(\"baz.h\", deps->nodes[1]->path());\n    ASSERT_EQ(other_out, log.nodes()[other_out->id()]);\n\n    // The file should have shrunk a bit for the smaller deps.\n#ifdef __USE_LARGEFILE64\n    struct stat64 st;\n    ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n    struct stat st;\n    ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n    file_size_3 = (int)st.st_size;\n    ASSERT_LT(file_size_3, file_size_2);\n  }\n\n  // Now reload the file and recompact with an empty manifest. The previous\n  // entries should be removed.\n  {\n    State state;\n    // Intentionally not parsing kManifest here.\n    DepsLog log;\n    string err;\n    ASSERT_TRUE(log.Load(kTestFilename, &state, &err));\n\n    Node* out = state.GetNode(\"out.o\", 0);\n    DepsLog::Deps* deps = log.GetDeps(out);\n    ASSERT_TRUE(deps);\n    ASSERT_EQ(1, deps->mtime);\n    ASSERT_EQ(1, deps->node_count);\n    ASSERT_EQ(\"foo.h\", deps->nodes[0]->path());\n\n    Node* other_out = state.GetNode(\"other_out.o\", 0);\n    deps = log.GetDeps(other_out);\n    ASSERT_TRUE(deps);\n    ASSERT_EQ(1, deps->mtime);\n    ASSERT_EQ(2, deps->node_count);\n    ASSERT_EQ(\"foo.h\", deps->nodes[0]->path());\n    ASSERT_EQ(\"baz.h\", deps->nodes[1]->path());\n\n    ASSERT_TRUE(log.Recompact(kTestFilename, &err));\n\n    // The previous entries should have been removed.\n    deps = log.GetDeps(out);\n    ASSERT_FALSE(deps);\n\n    deps = log.GetDeps(other_out);\n    ASSERT_FALSE(deps);\n\n    // The .h files pulled in via deps should no longer have ids either.\n    ASSERT_EQ(-1, state.LookupNode(\"foo.h\")->id());\n    ASSERT_EQ(-1, state.LookupNode(\"baz.h\")->id());\n\n    // The file should have shrunk more.\n#ifdef __USE_LARGEFILE64\n    struct stat64 st;\n    ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n    struct stat st;\n    ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n    int file_size_4 = (int)st.st_size;\n    ASSERT_LT(file_size_4, file_size_3);\n  }\n}\n\n// Verify that invalid file headers cause a new build.\nTEST_F(DepsLogTest, InvalidHeader) {\n  const char *kInvalidHeaders[] = {\n    \"\",                              // Empty file.\n    \"# ninjad\",                      // Truncated first line.\n    \"# ninjadeps\\n\",                 // No version int.\n    \"# ninjadeps\\n\\001\\002\",         // Truncated version int.\n    \"# ninjadeps\\n\\001\\002\\003\\004\"  // Invalid version int.\n  };\n  for (size_t i = 0; i < sizeof(kInvalidHeaders) / sizeof(kInvalidHeaders[0]);\n       ++i) {\n    FILE* deps_log = fopen(kTestFilename, \"wb\");\n    ASSERT_TRUE(deps_log != NULL);\n    ASSERT_EQ(\n        strlen(kInvalidHeaders[i]),\n        fwrite(kInvalidHeaders[i], 1, strlen(kInvalidHeaders[i]), deps_log));\n    ASSERT_EQ(0 ,fclose(deps_log));\n\n    string err;\n    DepsLog log;\n    State state;\n    ASSERT_TRUE(log.Load(kTestFilename, &state, &err));\n    EXPECT_EQ(\"bad deps log signature or version; starting over\", err);\n  }\n}\n\n// Simulate what happens when loading a truncated log file.\nTEST_F(DepsLogTest, Truncated) {\n  // Create a file with some entries.\n  {\n    State state;\n    DepsLog log;\n    string err;\n    EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar.h\", 0));\n    log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n\n    deps.clear();\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar2.h\", 0));\n    log.RecordDeps(state.GetNode(\"out2.o\", 0), 2, deps);\n\n    log.Close();\n  }\n\n  // Get the file size.\n#ifdef __USE_LARGEFILE64\n  struct stat64 st;\n  ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n  struct stat st;\n  ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n\n  // Try reloading at truncated sizes.\n  // Track how many nodes/deps were found; they should decrease with\n  // smaller sizes.\n  int node_count = 5;\n  int deps_count = 2;\n  for (int size = (int)st.st_size; size > 0; --size) {\n    string err;\n    ASSERT_TRUE(Truncate(kTestFilename, size, &err));\n\n    State state;\n    DepsLog log;\n    EXPECT_TRUE(log.Load(kTestFilename, &state, &err));\n    if (!err.empty()) {\n      // At some point the log will be so short as to be unparsable.\n      break;\n    }\n\n    ASSERT_GE(node_count, (int)log.nodes().size());\n    node_count = static_cast<int>(log.nodes().size());\n\n    // Count how many non-NULL deps entries there are.\n    int new_deps_count = 0;\n    for (vector<DepsLog::Deps*>::const_iterator i = log.deps().begin();\n         i != log.deps().end(); ++i) {\n      if (*i)\n        ++new_deps_count;\n    }\n    ASSERT_GE(deps_count, new_deps_count);\n    deps_count = new_deps_count;\n  }\n}\n\n// Run the truncation-recovery logic.\nTEST_F(DepsLogTest, TruncatedRecovery) {\n  // Create a file with some entries.\n  {\n    State state;\n    DepsLog log;\n    string err;\n    EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar.h\", 0));\n    log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n\n    deps.clear();\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar2.h\", 0));\n    log.RecordDeps(state.GetNode(\"out2.o\", 0), 2, deps);\n\n    log.Close();\n  }\n\n  // Shorten the file, corrupting the last record.\n  {\n#ifdef __USE_LARGEFILE64\n    struct stat64 st;\n    ASSERT_EQ(0, stat64(kTestFilename, &st));\n#else\n    struct stat st;\n    ASSERT_EQ(0, stat(kTestFilename, &st));\n#endif\n    string err;\n    ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err));\n  }\n\n  // Load the file again, add an entry.\n  {\n    State state;\n    DepsLog log;\n    string err;\n    EXPECT_TRUE(log.Load(kTestFilename, &state, &err));\n    ASSERT_EQ(\"premature end of file; recovering\", err);\n    err.clear();\n\n    // The truncated entry should've been discarded.\n    EXPECT_EQ(NULL, log.GetDeps(state.GetNode(\"out2.o\", 0)));\n\n    EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    // Add a new entry.\n    vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.h\", 0));\n    deps.push_back(state.GetNode(\"bar2.h\", 0));\n    log.RecordDeps(state.GetNode(\"out2.o\", 0), 3, deps);\n\n    log.Close();\n  }\n\n  // Load the file a third time to verify appending after a mangled\n  // entry doesn't break things.\n  {\n    State state;\n    DepsLog log;\n    string err;\n    EXPECT_TRUE(log.Load(kTestFilename, &state, &err));\n\n    // The truncated entry should exist.\n    DepsLog::Deps* deps = log.GetDeps(state.GetNode(\"out2.o\", 0));\n    ASSERT_TRUE(deps);\n  }\n}\n\nTEST_F(DepsLogTest, ReverseDepsNodes) {\n  State state;\n  DepsLog log;\n  string err;\n  EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));\n  ASSERT_EQ(\"\", err);\n\n  vector<Node*> deps;\n  deps.push_back(state.GetNode(\"foo.h\", 0));\n  deps.push_back(state.GetNode(\"bar.h\", 0));\n  log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n\n  deps.clear();\n  deps.push_back(state.GetNode(\"foo.h\", 0));\n  deps.push_back(state.GetNode(\"bar2.h\", 0));\n  log.RecordDeps(state.GetNode(\"out2.o\", 0), 2, deps);\n\n  log.Close();\n\n  Node* rev_deps = log.GetFirstReverseDepsNode(state.GetNode(\"foo.h\", 0));\n  EXPECT_TRUE(rev_deps == state.GetNode(\"out.o\", 0) ||\n              rev_deps == state.GetNode(\"out2.o\", 0));\n\n  rev_deps = log.GetFirstReverseDepsNode(state.GetNode(\"bar.h\", 0));\n  EXPECT_TRUE(rev_deps == state.GetNode(\"out.o\", 0));\n}\n\nTEST_F(DepsLogTest, MalformedDepsLog) {\n  std::string err;\n  {\n    State state;\n    DepsLog log;\n    EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));\n    ASSERT_EQ(\"\", err);\n\n    // First, create a valid log file.\n    std::vector<Node*> deps;\n    deps.push_back(state.GetNode(\"foo.hh\", 0));\n    deps.push_back(state.GetNode(\"bar.hpp\", 0));\n    log.RecordDeps(state.GetNode(\"out.o\", 0), 1, deps);\n    log.Close();\n  }\n\n  // Now read its value, validate it a little.\n  RealDiskInterface disk;\n\n  std::string original_contents;\n  ASSERT_EQ(FileReader::Okay, disk.ReadFile(kTestFilename,\n                                          &original_contents, &err));\n\n  const size_t version_offset = 12;\n  ASSERT_EQ(\"# ninjadeps\\n\", original_contents.substr(0, version_offset));\n#if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n  ASSERT_EQ('\\x04', original_contents[version_offset + 0]);\n  ASSERT_EQ('\\x00', original_contents[version_offset + 1]);\n  ASSERT_EQ('\\x00', original_contents[version_offset + 2]);\n  ASSERT_EQ('\\x00', original_contents[version_offset + 3]);\n#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n  ASSERT_EQ('\\x00', original_contents[version_offset + 0]);\n  ASSERT_EQ('\\x00', original_contents[version_offset + 1]);\n  ASSERT_EQ('\\x00', original_contents[version_offset + 2]);\n  ASSERT_EQ('\\x04', original_contents[version_offset + 3]);\n#else\n#  error \"Unknown endianness.\"\n#endif\n\n  // clang-format off\n  static const uint8_t kFirstRecord[] = {\n    // size field == 0x0000000c\n#if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n    0x0c, 0x00, 0x00, 0x00,\n#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n    0x00, 0x00, 0x00, 0x0c,\n#else\n#  error \"Unknown endianness.\"\n#endif\n    // name field = 'out.o' + 3 bytes of padding.\n    'o', 'u', 't', '.', 'o', 0x00, 0x00, 0x00,\n    // checksum = ~0\n    0xff, 0xff, 0xff, 0xff,\n  };\n  // clang-format on\n  const size_t kFirstRecordLen = sizeof(kFirstRecord);\n  const size_t first_offset = version_offset + 4;\n\n#define COMPARE_RECORD(start_pos, reference, len)  \\\n  ASSERT_EQ(original_contents.substr(start_pos, len), std::string(reinterpret_cast<const char*>(reference), len))\n\n  COMPARE_RECORD(first_offset, kFirstRecord, kFirstRecordLen);\n\n  const size_t second_offset = first_offset + kFirstRecordLen;\n  // clang-format off\n  static const uint8_t kSecondRecord[] = {\n    // size field == 0x0000000c\n#if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n    0x0c, 0x00, 0x00, 0x00,\n#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n    0x00, 0x00, 0x00, 0x0c,\n#else\n#  error \"Unknown endianness.\"\n#endif\n    // name field = 'foo.hh' + 2 bytes of padding.\n    'f', 'o', 'o', '.', 'h', 'h', 0x00, 0x00,\n    // checksum = ~1\n#if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n    0xfe, 0xff, 0xff, 0xff,\n#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n    0xff, 0xff, 0xff, 0xfe,\n#else\n#  error \"Unknown endianness.\"\n#endif\n  };\n  // clang-format on\n  const size_t kSecondRecordLen = sizeof(kSecondRecord);\n  COMPARE_RECORD(second_offset, kSecondRecord, kSecondRecordLen);\n\n  // Then start generating corrupted versions and trying to load them.\n  const char kBadLogFile[] = \"DepsLogTest-corrupted.tempfile\";\n\n  // Helper lambda to rewrite the bad log file with new content.\n  auto write_bad_log_file =\n      [&disk, &kBadLogFile](const std::string& bad_contents) -> bool {\n    (void)disk.RemoveFile(kBadLogFile);\n    return disk.WriteFile(kBadLogFile, bad_contents, false);\n  };\n\n  // First, corrupt the header.\n  std::string bad_contents = original_contents;\n  bad_contents[0] = '@';\n\n  ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);\n  {\n    State state;\n    DepsLog log;\n    err.clear();\n    ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));\n    ASSERT_EQ(\"bad deps log signature or version; starting over\", err);\n  }\n\n  // Second, truncate the version.\n  bad_contents = original_contents.substr(0, version_offset + 3);\n  ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);\n  {\n    State state;\n    DepsLog log;\n    err.clear();\n    ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));\n    ASSERT_EQ(\"bad deps log signature or version; starting over\", err);\n  }\n\n  // Truncate first record's |size| field. The loader should recover.\n  bad_contents = original_contents.substr(0, version_offset + 4 + 3);\n  ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);\n  {\n    State state;\n    DepsLog log;\n    err.clear();\n    ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));\n    ASSERT_EQ(\"\", err);\n  }\n\n  // Corrupt first record |size| value.\n  bad_contents = original_contents;\n#if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n  bad_contents[first_offset + 0] = '\\x55';\n  bad_contents[first_offset + 1] = '\\xaa';\n  bad_contents[first_offset + 2] = '\\xff';\n  bad_contents[first_offset + 3] = '\\xff';\n#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n  bad_contents[first_offset + 0] = '\\xff';\n  bad_contents[first_offset + 1] = '\\xff';\n  bad_contents[first_offset + 2] = '\\xaa';\n  bad_contents[first_offset + 3] = '\\x55';\n#else\n#  error \"Unknown endianness.\"\n#endif\n  ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);\n  {\n    State state;\n    DepsLog log;\n    err.clear();\n    ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));\n    ASSERT_EQ(\"premature end of file; recovering\", err);\n  }\n\n  // Make first record |size| less than 4.\n  bad_contents = original_contents;\n  bad_contents[first_offset] = '\\x01';\n  ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);\n  {\n    State state;\n    DepsLog log;\n    err.clear();\n    ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));\n    ASSERT_EQ(\"premature end of file; recovering\", err);\n  }\n}\n\n}  // anonymous namespace\n"
  },
  {
    "path": "src/disk_interface.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"disk_interface.h\"\n\n#include <algorithm>\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#ifdef _WIN32\n#include <direct.h>  // _mkdir\n#include <windows.h>\n\n#include <sstream>\n#else\n#include <unistd.h>\n#endif\n\n#include \"metrics.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nnamespace {\n\nstring DirName(const string& path) {\n#ifdef _WIN32\n  static const char kPathSeparators[] = \"\\\\/\";\n#else\n  static const char kPathSeparators[] = \"/\";\n#endif\n  static const char* const kEnd = kPathSeparators + sizeof(kPathSeparators) - 1;\n\n  string::size_type slash_pos = path.find_last_of(kPathSeparators);\n  if (slash_pos == string::npos)\n    return string();  // Nothing to do.\n  while (slash_pos > 0 &&\n         std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd)\n    --slash_pos;\n  return path.substr(0, slash_pos);\n}\n\nint MakeDir(const string& path) {\n#ifdef _WIN32\n  return _mkdir(path.c_str());\n#else\n  return mkdir(path.c_str(), 0777);\n#endif\n}\n\n#ifdef _WIN32\nTimeStamp TimeStampFromFileTime(const FILETIME& filetime) {\n  // FILETIME is in 100-nanosecond increments since the Windows epoch.\n  // We don't much care about epoch correctness but we do want the\n  // resulting value to fit in a 64-bit integer.\n  uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) |\n    ((uint64_t)filetime.dwLowDateTime);\n  // 1600 epoch -> 2000 epoch (subtract 400 years).\n  return (TimeStamp)mtime - 12622770400LL * (1000000000LL / 100);\n}\n\nTimeStamp StatSingleFile(const string& path, string* err) {\n  WIN32_FILE_ATTRIBUTE_DATA attrs;\n  if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) {\n    DWORD win_err = GetLastError();\n    if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)\n      return 0;\n    *err = \"GetFileAttributesEx(\" + path + \"): \" + GetLastErrorString();\n    return -1;\n  }\n\n  if (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {\n    HANDLE hFile = CreateFileA(path.c_str(), 0, 0, 0, OPEN_EXISTING,\n                               FILE_FLAG_BACKUP_SEMANTICS, 0);\n    if (hFile == INVALID_HANDLE_VALUE) {\n      DWORD win_err = GetLastError();\n      if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)\n        return 0;\n      *err = \"CreateFileA(\" + path + \"): \" + GetLastErrorString();\n      CloseHandle(hFile);\n      return -1;\n    }\n\n    CHAR pathBuf[MAX_PATH];\n    if (GetFinalPathNameByHandleA(hFile, pathBuf, MAX_PATH,\n                                  FILE_NAME_NORMALIZED) == 0) {\n      DWORD win_err = GetLastError();\n      if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)\n        return 0;\n      *err = \"GetFinalPathNameByHandleA(\" + path + \"): \" + GetLastErrorString();\n      CloseHandle(hFile);\n      return -1;\n    }\n    CloseHandle(hFile);\n    return StatSingleFile(pathBuf, err);\n  }\n\n  return TimeStampFromFileTime(attrs.ftLastWriteTime);\n}\n\nbool IsWindows7OrLater() {\n  OSVERSIONINFOEX version_info =\n      { sizeof(OSVERSIONINFOEX), 6, 1, 0, 0, {0}, 0, 0, 0, 0, 0};\n  DWORDLONG comparison = 0;\n  VER_SET_CONDITION(comparison, VER_MAJORVERSION, VER_GREATER_EQUAL);\n  VER_SET_CONDITION(comparison, VER_MINORVERSION, VER_GREATER_EQUAL);\n  return VerifyVersionInfo(\n      &version_info, VER_MAJORVERSION | VER_MINORVERSION, comparison);\n}\n\nbool StatAllFilesInDir(const string& dir, map<string, TimeStamp>* stamps,\n                       string* err) {\n  // FindExInfoBasic is 30% faster than FindExInfoStandard.\n  static bool can_use_basic_info = IsWindows7OrLater();\n  // This is not in earlier SDKs.\n  const FINDEX_INFO_LEVELS kFindExInfoBasic =\n      static_cast<FINDEX_INFO_LEVELS>(1);\n  FINDEX_INFO_LEVELS level =\n      can_use_basic_info ? kFindExInfoBasic : FindExInfoStandard;\n  WIN32_FIND_DATAA ffd;\n  HANDLE find_handle = FindFirstFileExA((dir + \"\\\\*\").c_str(), level, &ffd,\n                                        FindExSearchNameMatch, NULL, 0);\n\n  if (find_handle == INVALID_HANDLE_VALUE) {\n    DWORD win_err = GetLastError();\n    if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND ||\n        win_err == ERROR_DIRECTORY)\n      return true;\n    *err = \"FindFirstFileExA(\" + dir + \"): \" + GetLastErrorString();\n    return false;\n  }\n  do {\n    string lowername = ffd.cFileName;\n    if (lowername == \"..\") {\n      // Seems to just copy the timestamp for \"..\" from \".\", which is wrong.\n      // This is the case at least on NTFS under Windows 7.\n      continue;\n    }\n\n    transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);\n\n    if (ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {\n      // File is a symlink, stat the linked file.\n      stamps->insert(make_pair(\n          lowername, StatSingleFile(dir + \"\\\\\" + ffd.cFileName, err)));\n    } else {\n      stamps->insert(\n          make_pair(lowername, TimeStampFromFileTime(ffd.ftLastWriteTime)));\n    }\n\n  } while (FindNextFileA(find_handle, &ffd));\n  FindClose(find_handle);\n  return true;\n}\n#endif  // _WIN32\n\n}  // namespace\n\n// DiskInterface ---------------------------------------------------------------\n\nbool DiskInterface::MakeDirs(const string& path) {\n  string dir = DirName(path);\n  if (dir.empty())\n    return true;  // Reached root; assume it's there.\n  string err;\n  TimeStamp mtime = Stat(dir, &err);\n  if (mtime < 0) {\n    Error(\"%s\", err.c_str());\n    return false;\n  }\n  if (mtime > 0)\n    return true;  // Exists already; we're done.\n\n  // Directory doesn't exist.  Try creating its parent first.\n  bool success = MakeDirs(dir);\n  if (!success)\n    return false;\n  return MakeDir(dir);\n}\n\n// RealDiskInterface -----------------------------------------------------------\nRealDiskInterface::RealDiskInterface()\n#ifdef _WIN32\n: use_cache_(false), long_paths_enabled_(false) {\n  // Probe ntdll.dll for RtlAreLongPathsEnabled, and call it if it exists.\n  HINSTANCE ntdll_lib = ::GetModuleHandleW(L\"ntdll\");\n  if (ntdll_lib) {\n    typedef BOOLEAN(WINAPI FunctionType)();\n    auto* func_ptr = FunctionCast<FunctionType*>(\n        ::GetProcAddress(ntdll_lib, \"RtlAreLongPathsEnabled\"));\n    if (func_ptr) {\n      long_paths_enabled_ = (*func_ptr)();\n    }\n  }\n}\n#else\n{}\n#endif\n\nTimeStamp RealDiskInterface::Stat(const string& path, string* err) const {\n  METRIC_RECORD(\"node stat\");\n#ifdef _WIN32\n  // MSDN: \"Naming Files, Paths, and Namespaces\"\n  // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx\n  if (!path.empty() && !AreLongPathsEnabled() && path[0] != '\\\\' &&\n      path.size() > MAX_PATH) {\n    ostringstream err_stream;\n    err_stream << \"Stat(\" << path << \"): Filename longer than \" << MAX_PATH\n               << \" characters\";\n    *err = err_stream.str();\n    return -1;\n  }\n  if (!use_cache_)\n    return StatSingleFile(path, err);\n\n  string dir = DirName(path);\n  string base(path.substr(dir.size() ? dir.size() + 1 : 0));\n  if (base == \"..\") {\n    // StatAllFilesInDir does not report any information for base = \"..\".\n    base = \".\";\n    dir = path;\n  }\n\n  string dir_lowercase = dir;\n  transform(dir.begin(), dir.end(), dir_lowercase.begin(), ::tolower);\n  transform(base.begin(), base.end(), base.begin(), ::tolower);\n\n  Cache::iterator ci = cache_.find(dir_lowercase);\n  if (ci == cache_.end()) {\n    ci = cache_.insert(make_pair(dir_lowercase, DirCache())).first;\n    if (!StatAllFilesInDir(dir.empty() ? \".\" : dir, &ci->second, err)) {\n      cache_.erase(ci);\n      return -1;\n    }\n  }\n  DirCache::iterator di = ci->second.find(base);\n  return di != ci->second.end() ? di->second : 0;\n#else\n#ifdef __USE_LARGEFILE64\n  struct stat64 st;\n  if (stat64(path.c_str(), &st) < 0) {\n#else\n  struct stat st;\n  if (stat(path.c_str(), &st) < 0) {\n#endif\n    if (errno == ENOENT || errno == ENOTDIR)\n      return 0;\n    *err = \"stat(\" + path + \"): \" + strerror(errno);\n    return -1;\n  }\n  // Some users (Flatpak) set mtime to 0, this should be harmless\n  // and avoids conflicting with our return value of 0 meaning\n  // that it doesn't exist.\n  if (st.st_mtime == 0)\n    return 1;\n#if defined(_AIX)\n  return (int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n;\n#elif defined(__APPLE__)\n  return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL +\n          st.st_mtimespec.tv_nsec);\n#elif defined(st_mtime) // A macro, so we're likely on modern POSIX.\n  return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec;\n#else\n  return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec;\n#endif\n#endif\n}\n\nbool RealDiskInterface::WriteFile(const string& path, const string& contents,\n                                  bool crlf_on_windows) {\n  FILE* fp = fopen(path.c_str(),\n#ifdef _WIN32\n                   crlf_on_windows ? \"w\" : \"wb\");\n#else\n                   \"wb\");\n  (void)crlf_on_windows;\n#endif\n  if (fp == NULL) {\n    Error(\"WriteFile(%s): Unable to create file. %s\",\n          path.c_str(), strerror(errno));\n    return false;\n  }\n\n  if (fwrite(contents.data(), 1, contents.length(), fp) < contents.length())  {\n    Error(\"WriteFile(%s): Unable to write to the file. %s\",\n          path.c_str(), strerror(errno));\n    fclose(fp);\n    return false;\n  }\n\n  if (fclose(fp) == EOF) {\n    Error(\"WriteFile(%s): Unable to close the file. %s\",\n          path.c_str(), strerror(errno));\n    return false;\n  }\n\n  return true;\n}\n\nbool RealDiskInterface::MakeDir(const string& path) {\n  if (::MakeDir(path) < 0) {\n    if (errno == EEXIST) {\n      return true;\n    }\n    Error(\"mkdir(%s): %s\", path.c_str(), strerror(errno));\n    return false;\n  }\n  return true;\n}\n\nFileReader::Status RealDiskInterface::ReadFile(const string& path,\n                                               string* contents,\n                                               string* err) {\n  switch (::ReadFile(path, contents, err)) {\n  case 0:       return Okay;\n  case -ENOENT: return NotFound;\n  default:      return OtherError;\n  }\n}\n\nint RealDiskInterface::RemoveFile(const string& path) {\n#ifdef _WIN32\n  DWORD attributes = GetFileAttributesA(path.c_str());\n  if (attributes == INVALID_FILE_ATTRIBUTES) {\n    DWORD win_err = GetLastError();\n    if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) {\n      return 1;\n    }\n  } else if (attributes & FILE_ATTRIBUTE_READONLY) {\n    // On non-Windows systems, remove() will happily delete read-only files.\n    // On Windows Ninja should behave the same:\n    //   https://github.com/ninja-build/ninja/issues/1886\n    // Skip error checking.  If this fails, accept whatever happens below.\n    SetFileAttributesA(path.c_str(), attributes & ~FILE_ATTRIBUTE_READONLY);\n  }\n  if (attributes & FILE_ATTRIBUTE_DIRECTORY) {\n    // remove() deletes both files and directories. On Windows we have to\n    // select the correct function (DeleteFile will yield Permission Denied when\n    // used on a directory)\n    // This fixes the behavior of ninja -t clean in some cases\n    // https://github.com/ninja-build/ninja/issues/828\n    if (!RemoveDirectoryA(path.c_str())) {\n      DWORD win_err = GetLastError();\n      if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) {\n        return 1;\n      }\n      // Report remove(), not RemoveDirectory(), for cross-platform consistency.\n      Error(\"remove(%s): %s\", path.c_str(), GetLastErrorString().c_str());\n      return -1;\n    }\n  } else {\n    if (!DeleteFileA(path.c_str())) {\n      DWORD win_err = GetLastError();\n      if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) {\n        return 1;\n      }\n      // Report as remove(), not DeleteFile(), for cross-platform consistency.\n      Error(\"remove(%s): %s\", path.c_str(), GetLastErrorString().c_str());\n      return -1;\n    }\n  }\n#else\n  if (remove(path.c_str()) < 0) {\n    switch (errno) {\n      case ENOENT:\n        return 1;\n      default:\n        Error(\"remove(%s): %s\", path.c_str(), strerror(errno));\n        return -1;\n    }\n  }\n#endif\n  return 0;\n}\n\nvoid RealDiskInterface::AllowStatCache(bool allow) {\n#ifdef _WIN32\n  use_cache_ = allow;\n  if (!use_cache_)\n    cache_.clear();\n#endif\n}\n\n#ifdef _WIN32\nbool RealDiskInterface::AreLongPathsEnabled(void) const {\n  return long_paths_enabled_;\n}\n#endif\n"
  },
  {
    "path": "src/disk_interface.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_DISK_INTERFACE_H_\n#define NINJA_DISK_INTERFACE_H_\n\n#include <map>\n#include <string>\n\n#include \"timestamp.h\"\n\n/// Interface for reading files from disk.  See DiskInterface for details.\n/// This base offers the minimum interface needed just to read files.\nstruct FileReader {\n  virtual ~FileReader() {}\n\n  /// Result of ReadFile.\n  enum Status {\n    Okay,\n    NotFound,\n    OtherError\n  };\n\n  /// Read and store in given string.  On success, return Okay.\n  /// On error, return another Status and fill |err|.\n  virtual Status ReadFile(const std::string& path, std::string* contents,\n                          std::string* err) = 0;\n};\n\n/// Interface for accessing the disk.\n///\n/// Abstract so it can be mocked out for tests.  The real implementation\n/// is RealDiskInterface.\nstruct DiskInterface: public FileReader {\n  /// stat() a file, returning the mtime, or 0 if missing and -1 on\n  /// other errors.\n  virtual TimeStamp Stat(const std::string& path, std::string* err) const = 0;\n\n  /// Create a directory, returning false on failure.\n  virtual bool MakeDir(const std::string& path) = 0;\n\n  /// Create a file, with the specified name and contents\n  /// If \\a crlf_on_windows is true, \\n will be converted to \\r\\n (only on\n  /// Windows builds of Ninja).\n  /// Returns true on success, false on failure\n  virtual bool WriteFile(const std::string& path, const std::string& contents,\n                         bool crlf_on_windows) = 0;\n\n  /// Remove the file named @a path. It behaves like 'rm -f path' so no errors\n  /// are reported if it does not exists.\n  /// @returns 0 if the file has been removed,\n  ///          1 if the file does not exist, and\n  ///          -1 if an error occurs.\n  virtual int RemoveFile(const std::string& path) = 0;\n\n  /// Create all the parent directories for path; like mkdir -p\n  /// `basename path`.\n  bool MakeDirs(const std::string& path);\n};\n\n/// Implementation of DiskInterface that actually hits the disk.\nstruct RealDiskInterface : public DiskInterface {\n  RealDiskInterface();\n  virtual ~RealDiskInterface() {}\n  TimeStamp Stat(const std::string& path, std::string* err) const override;\n  bool MakeDir(const std::string& path) override;\n  bool WriteFile(const std::string& path, const std::string& contents,\n                 bool crlf_on_windows) override;\n  Status ReadFile(const std::string& path, std::string* contents,\n                  std::string* err) override;\n  int RemoveFile(const std::string& path) override;\n\n  /// Whether stat information can be cached.  Only has an effect on Windows.\n  void AllowStatCache(bool allow);\n\n#ifdef _WIN32\n  /// Whether long paths are enabled.  Only has an effect on Windows.\n  bool AreLongPathsEnabled() const;\n#endif\n\n private:\n#ifdef _WIN32\n  /// Whether stat information can be cached.\n  bool use_cache_;\n\n  /// Whether long paths are enabled.\n  bool long_paths_enabled_;\n\n  typedef std::map<std::string, TimeStamp> DirCache;\n  // TODO: Neither a map nor a hashmap seems ideal here.  If the statcache\n  // works out, come up with a better data structure.\n  typedef std::map<std::string, DirCache> Cache;\n  mutable Cache cache_;\n#endif\n};\n\n#endif  // NINJA_DISK_INTERFACE_H_\n"
  },
  {
    "path": "src/disk_interface_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <assert.h>\n#include <stdio.h>\n#ifdef _WIN32\n#include <io.h>\n#include <windows.h>\n#include <direct.h>\n#endif\n\n#include \"disk_interface.h\"\n#include \"graph.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nnamespace {\n\nstruct DiskInterfaceTest : public testing::Test {\n  virtual void SetUp() {\n    // These tests do real disk accesses, so create a temp dir.\n    temp_dir_.CreateAndEnter(\"Ninja-DiskInterfaceTest\");\n  }\n\n  virtual void TearDown() {\n    temp_dir_.Cleanup();\n  }\n\n  bool Touch(const char* path) {\n    FILE *f = fopen(path, \"w\");\n    if (!f)\n      return false;\n    return fclose(f) == 0;\n  }\n\n  ScopedTempDir temp_dir_;\n  RealDiskInterface disk_;\n};\n\nTEST_F(DiskInterfaceTest, StatMissingFile) {\n  string err;\n  EXPECT_EQ(0, disk_.Stat(\"nosuchfile\", &err));\n  EXPECT_EQ(\"\", err);\n\n  // On Windows, the errno for a file in a nonexistent directory\n  // is different.\n  EXPECT_EQ(0, disk_.Stat(\"nosuchdir/nosuchfile\", &err));\n  EXPECT_EQ(\"\", err);\n\n  // On POSIX systems, the errno is different if a component of the\n  // path prefix is not a directory.\n  ASSERT_TRUE(Touch(\"notadir\"));\n  EXPECT_EQ(0, disk_.Stat(\"notadir/nosuchfile\", &err));\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(DiskInterfaceTest, StatMissingFileWithCache) {\n  disk_.AllowStatCache(true);\n  string err;\n\n  // On Windows, the errno for FindFirstFileExA, which is used when the stat\n  // cache is enabled, is different when the directory name is not a directory.\n  ASSERT_TRUE(Touch(\"notadir\"));\n  EXPECT_EQ(0, disk_.Stat(\"notadir/nosuchfile\", &err));\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(DiskInterfaceTest, StatBadPath) {\n  string err;\n#ifdef _WIN32\n  string bad_path(\"cc:\\\\foo\");\n  EXPECT_EQ(-1, disk_.Stat(bad_path, &err));\n  EXPECT_NE(\"\", err);\n#else\n  string too_long_name(512, 'x');\n  EXPECT_EQ(-1, disk_.Stat(too_long_name, &err));\n  EXPECT_NE(\"\", err);\n#endif\n}\n\nTEST_F(DiskInterfaceTest, StatExistingFile) {\n  string err;\n  ASSERT_TRUE(Touch(\"file\"));\n  EXPECT_GT(disk_.Stat(\"file\", &err), 1);\n  EXPECT_EQ(\"\", err);\n}\n\n#ifdef _WIN32\nTEST_F(DiskInterfaceTest, StatExistingFileWithLongPath) {\n  string err;\n  char currentdir[32767];\n  _getcwd(currentdir, sizeof(currentdir));\n  const string filename = string(currentdir) +\n\"\\\\filename_with_256_characters_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\\\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\\\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\\\nxxxxxxxxxxxxxxxxxxxxx\";\n  const string prefixed = \"\\\\\\\\?\\\\\" + filename;\n  ASSERT_TRUE(Touch(prefixed.c_str()));\n  EXPECT_GT(disk_.Stat(disk_.AreLongPathsEnabled() ?\n    filename : prefixed, &err), 1);\n  EXPECT_EQ(\"\", err);\n}\n#endif\n\nTEST_F(DiskInterfaceTest, StatSymlink) {\n  string err;\n\n  ASSERT_TRUE(Touch(\"file\"));\n  auto fileTimeStamp = disk_.Stat(\"file\", &err);\n  EXPECT_EQ(\"\", err);\n\n#ifdef _WIN32\n  // Create a symlink to file\n  ASSERT_TRUE(CreateSymbolicLinkA(\n      \"fileSymlink\", \"file\", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE));\n#else\n  ASSERT_TRUE(symlink(\"file\", \"fileSymlink\") == 0);\n#endif\n\n  // Assert that stating the symlink will resolve the timestamp for the\n  // linked file.\n  auto symlinkTimeStamp = disk_.Stat(\"fileSymlink\", &err);\n  ASSERT_EQ(fileTimeStamp, symlinkTimeStamp);\n}\n\nTEST_F(DiskInterfaceTest, StatExistingDir) {\n  string err;\n  ASSERT_TRUE(disk_.MakeDir(\"subdir\"));\n  ASSERT_TRUE(disk_.MakeDir(\"subdir/subsubdir\"));\n  EXPECT_GT(disk_.Stat(\"..\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\".\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\"subdir\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\"subdir/subsubdir\", &err), 1);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_EQ(disk_.Stat(\"subdir\", &err),\n            disk_.Stat(\"subdir/.\", &err));\n  EXPECT_EQ(disk_.Stat(\"subdir\", &err),\n            disk_.Stat(\"subdir/subsubdir/..\", &err));\n  EXPECT_EQ(disk_.Stat(\"subdir/subsubdir\", &err),\n            disk_.Stat(\"subdir/subsubdir/.\", &err));\n}\n\n#ifdef _WIN32\nTEST_F(DiskInterfaceTest, StatCache) {\n  string err;\n\n  ASSERT_TRUE(Touch(\"file1\"));\n  ASSERT_TRUE(Touch(\"fiLE2\"));\n  ASSERT_TRUE(disk_.MakeDir(\"subdir\"));\n  ASSERT_TRUE(disk_.MakeDir(\"subdir/subsubdir\"));\n  ASSERT_TRUE(Touch(\"subdir\\\\subfile1\"));\n  ASSERT_TRUE(Touch(\"subdir\\\\SUBFILE2\"));\n  ASSERT_TRUE(Touch(\"subdir\\\\SUBFILE3\"));\n\n  disk_.AllowStatCache(false);\n  TimeStamp parent_stat_uncached = disk_.Stat(\"..\", &err);\n  disk_.AllowStatCache(true);\n\n  EXPECT_GT(disk_.Stat(\"FIle1\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\"file1\", &err), 1);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_GT(disk_.Stat(\"subdir/subfile2\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\"sUbdir\\\\suBFile1\", &err), 1);\n  EXPECT_EQ(\"\", err);\n\n  EXPECT_GT(disk_.Stat(\"..\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\".\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\"subdir\", &err), 1);\n  EXPECT_EQ(\"\", err);\n  EXPECT_GT(disk_.Stat(\"subdir/subsubdir\", &err), 1);\n  EXPECT_EQ(\"\", err);\n\n#ifndef _MSC_VER // TODO: Investigate why. Also see https://github.com/ninja-build/ninja/pull/1423\n  EXPECT_EQ(disk_.Stat(\"subdir\", &err),\n            disk_.Stat(\"subdir/.\", &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(disk_.Stat(\"subdir\", &err),\n            disk_.Stat(\"subdir/subsubdir/..\", &err));\n#endif\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(disk_.Stat(\"..\", &err), parent_stat_uncached);\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(disk_.Stat(\"subdir/subsubdir\", &err),\n            disk_.Stat(\"subdir/subsubdir/.\", &err));\n  EXPECT_EQ(\"\", err);\n\n  // Test error cases.\n  string bad_path(\"cc:\\\\foo\");\n  EXPECT_EQ(-1, disk_.Stat(bad_path, &err));\n  EXPECT_NE(\"\", err); err.clear();\n  EXPECT_EQ(-1, disk_.Stat(bad_path, &err));\n  EXPECT_NE(\"\", err); err.clear();\n  EXPECT_EQ(0, disk_.Stat(\"nosuchfile\", &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(0, disk_.Stat(\"nosuchdir/nosuchfile\", &err));\n  EXPECT_EQ(\"\", err);\n}\n#endif\n\nTEST_F(DiskInterfaceTest, ReadFile) {\n  string err;\n  std::string content;\n  ASSERT_EQ(DiskInterface::NotFound,\n            disk_.ReadFile(\"foobar\", &content, &err));\n  EXPECT_EQ(\"\", content);\n  EXPECT_NE(\"\", err); // actual value is platform-specific\n  err.clear();\n\n  const char* kTestFile = \"testfile\";\n  FILE* f = fopen(kTestFile, \"wb\");\n  ASSERT_TRUE(f);\n  const char* kTestContent = \"test content\\nok\";\n  fprintf(f, \"%s\", kTestContent);\n  ASSERT_EQ(0, fclose(f));\n\n  ASSERT_EQ(DiskInterface::Okay,\n            disk_.ReadFile(kTestFile, &content, &err));\n  EXPECT_EQ(kTestContent, content);\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(DiskInterfaceTest, MakeDirs) {\n  string path = \"path/with/double//slash/\";\n  EXPECT_TRUE(disk_.MakeDirs(path));\n  FILE* f = fopen((path + \"a_file\").c_str(), \"w\");\n  EXPECT_TRUE(f);\n  EXPECT_EQ(0, fclose(f));\n#ifdef _WIN32\n  string path2 = \"another\\\\with\\\\back\\\\\\\\slashes\\\\\";\n  EXPECT_TRUE(disk_.MakeDirs(path2));\n  FILE* f2 = fopen((path2 + \"a_file\").c_str(), \"w\");\n  EXPECT_TRUE(f2);\n  EXPECT_EQ(0, fclose(f2));\n#endif\n}\n\nTEST_F(DiskInterfaceTest, RemoveFile) {\n  const char* kFileName = \"file-to-remove\";\n  ASSERT_TRUE(Touch(kFileName));\n  EXPECT_EQ(0, disk_.RemoveFile(kFileName));\n  EXPECT_EQ(1, disk_.RemoveFile(kFileName));\n  EXPECT_EQ(1, disk_.RemoveFile(\"does not exist\"));\n#ifdef _WIN32\n  ASSERT_TRUE(Touch(kFileName));\n  EXPECT_EQ(0, system((std::string(\"attrib +R \") + kFileName).c_str()));\n  EXPECT_EQ(0, disk_.RemoveFile(kFileName));\n  EXPECT_EQ(1, disk_.RemoveFile(kFileName));\n#endif\n}\n\nTEST_F(DiskInterfaceTest, RemoveDirectory) {\n  const char* kDirectoryName = \"directory-to-remove\";\n  EXPECT_TRUE(disk_.MakeDir(kDirectoryName));\n  EXPECT_EQ(0, disk_.RemoveFile(kDirectoryName));\n  EXPECT_EQ(1, disk_.RemoveFile(kDirectoryName));\n  EXPECT_EQ(1, disk_.RemoveFile(\"does not exist\"));\n}\n\nstruct StatTest : public StateTestWithBuiltinRules,\n                  public DiskInterface {\n  StatTest() : scan_(&state_, NULL, NULL, this, NULL, NULL) {}\n\n  // DiskInterface implementation.\n  TimeStamp Stat(const string& path, string* err) const override;\n  bool WriteFile(const string& path, const string& contents,\n                 bool /*crlf_on_windows*/) override {\n    assert(false);\n    return true;\n  }\n  bool MakeDir(const string& path) override {\n    assert(false);\n    return false;\n  }\n  Status ReadFile(const string& path, string* contents, string* err) override {\n    assert(false);\n    return NotFound;\n  }\n  int RemoveFile(const string& path) override {\n    assert(false);\n    return 0;\n  }\n\n  DependencyScan scan_;\n  map<string, TimeStamp> mtimes_;\n  mutable vector<string> stats_;\n};\n\nTimeStamp StatTest::Stat(const string& path, string* err) const {\n  stats_.push_back(path);\n  map<string, TimeStamp>::const_iterator i = mtimes_.find(path);\n  if (i == mtimes_.end())\n    return 0;  // File not found.\n  return i->second;\n}\n\nTEST_F(StatTest, Simple) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat in\\n\"));\n\n  Node* out = GetNode(\"out\");\n  string err;\n  EXPECT_TRUE(out->Stat(this, &err));\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, stats_.size());\n  scan_.RecomputeDirty(out, NULL, NULL);\n  ASSERT_EQ(2u, stats_.size());\n  ASSERT_EQ(\"out\", stats_[0]);\n  ASSERT_EQ(\"in\",  stats_[1]);\n}\n\nTEST_F(StatTest, TwoStep) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat mid\\n\"\n\"build mid: cat in\\n\"));\n\n  Node* out = GetNode(\"out\");\n  string err;\n  EXPECT_TRUE(out->Stat(this, &err));\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, stats_.size());\n  scan_.RecomputeDirty(out, NULL, NULL);\n  ASSERT_EQ(3u, stats_.size());\n  ASSERT_EQ(\"out\", stats_[0]);\n  ASSERT_TRUE(GetNode(\"out\")->dirty());\n  ASSERT_EQ(\"mid\",  stats_[1]);\n  ASSERT_TRUE(GetNode(\"mid\")->dirty());\n  ASSERT_EQ(\"in\",  stats_[2]);\n}\n\nTEST_F(StatTest, Tree) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat mid1 mid2\\n\"\n\"build mid1: cat in11 in12\\n\"\n\"build mid2: cat in21 in22\\n\"));\n\n  Node* out = GetNode(\"out\");\n  string err;\n  EXPECT_TRUE(out->Stat(this, &err));\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, stats_.size());\n  scan_.RecomputeDirty(out, NULL, NULL);\n  ASSERT_EQ(1u + 6u, stats_.size());\n  ASSERT_EQ(\"mid1\", stats_[1]);\n  ASSERT_TRUE(GetNode(\"mid1\")->dirty());\n  ASSERT_EQ(\"in11\", stats_[2]);\n}\n\nTEST_F(StatTest, Middle) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat mid\\n\"\n\"build mid: cat in\\n\"));\n\n  mtimes_[\"in\"] = 1;\n  mtimes_[\"mid\"] = 0;  // missing\n  mtimes_[\"out\"] = 1;\n\n  Node* out = GetNode(\"out\");\n  string err;\n  EXPECT_TRUE(out->Stat(this, &err));\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(1u, stats_.size());\n  scan_.RecomputeDirty(out, NULL, NULL);\n  ASSERT_FALSE(GetNode(\"in\")->dirty());\n  ASSERT_TRUE(GetNode(\"mid\")->dirty());\n  ASSERT_TRUE(GetNode(\"out\")->dirty());\n}\n\n}  // namespace\n"
  },
  {
    "path": "src/dyndep.cc",
    "content": "// Copyright 2015 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"dyndep.h\"\n\n#include <assert.h>\n#include <stdio.h>\n\n#include \"debug_flags.h\"\n#include \"disk_interface.h\"\n#include \"dyndep_parser.h\"\n#include \"explanations.h\"\n#include \"graph.h\"\n#include \"state.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nbool DyndepLoader::LoadDyndeps(Node* node, std::string* err) const {\n  DyndepFile ddf;\n  return LoadDyndeps(node, &ddf, err);\n}\n\nbool DyndepLoader::LoadDyndeps(Node* node, DyndepFile* ddf,\n                               std::string* err) const {\n  // We are loading the dyndep file now so it is no longer pending.\n  node->set_dyndep_pending(false);\n\n  // Load the dyndep information from the file.\n  explanations_.Record(node, \"loading dyndep file '%s'\", node->path().c_str());\n\n  if (!LoadDyndepFile(node, ddf, err))\n    return false;\n\n  // Update each edge that specified this node as its dyndep binding.\n  std::vector<Edge*> const& out_edges = node->out_edges();\n  for (Edge* edge : out_edges) {\n    if (edge->dyndep_ != node)\n      continue;\n\n    DyndepFile::iterator ddi = ddf->find(edge);\n    if (ddi == ddf->end()) {\n      *err = (\"'\" + edge->outputs_[0]->path() + \"' \"\n              \"not mentioned in its dyndep file \"\n              \"'\" + node->path() + \"'\");\n      return false;\n    }\n\n    ddi->second.used_ = true;\n    Dyndeps const& dyndeps = ddi->second;\n    if (!UpdateEdge(edge, &dyndeps, err)) {\n      return false;\n    }\n  }\n\n  // Reject extra outputs in dyndep file.\n  for (const auto& dyndep_output : *ddf) {\n    if (!dyndep_output.second.used_) {\n      Edge* const edge = dyndep_output.first;\n      *err = (\"dyndep file '\" + node->path() + \"' mentions output \"\n              \"'\" + edge->outputs_[0]->path() + \"' whose build statement \"\n              \"does not have a dyndep binding for the file\");\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool DyndepLoader::UpdateEdge(Edge* edge, Dyndeps const* dyndeps,\n                              std::string* err) const {\n  // Add dyndep-discovered bindings to the edge.\n  // We know the edge already has its own binding\n  // scope because it has a \"dyndep\" binding.\n  if (dyndeps->restat_)\n    edge->env_->AddBinding(\"restat\", \"1\");\n\n  // Add the dyndep-discovered outputs to the edge.\n  edge->outputs_.insert(edge->outputs_.end(),\n                        dyndeps->implicit_outputs_.begin(),\n                        dyndeps->implicit_outputs_.end());\n  edge->implicit_outs_ += dyndeps->implicit_outputs_.size();\n\n  // Add this edge as incoming to each new output.\n  for (Node* node : dyndeps->implicit_outputs_) {\n    if (node->in_edge()) {\n      // This node already has an edge producing it.\n      *err = \"multiple rules generate \" + node->path();\n      return false;\n    }\n    node->set_in_edge(edge);\n  }\n\n  // Add the dyndep-discovered inputs to the edge.\n  edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_,\n                       dyndeps->implicit_inputs_.begin(),\n                       dyndeps->implicit_inputs_.end());\n  edge->implicit_deps_ += dyndeps->implicit_inputs_.size();\n\n  // Add this edge as outgoing from each new input.\n  for (Node* node : dyndeps->implicit_inputs_)\n    node->AddOutEdge(edge);\n\n  return true;\n}\n\nbool DyndepLoader::LoadDyndepFile(Node* file, DyndepFile* ddf,\n                                  std::string* err) const {\n  DyndepParser parser(state_, disk_interface_, ddf);\n  return parser.Load(file->path(), err);\n}\n"
  },
  {
    "path": "src/dyndep.h",
    "content": "// Copyright 2015 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_DYNDEP_LOADER_H_\n#define NINJA_DYNDEP_LOADER_H_\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"explanations.h\"\n\nstruct DiskInterface;\nstruct Edge;\nstruct Node;\nstruct State;\n\n/// Store dynamically-discovered dependency information for one edge.\nstruct Dyndeps {\n  Dyndeps() : used_(false), restat_(false) {}\n  bool used_;\n  bool restat_;\n  std::vector<Node*> implicit_inputs_;\n  std::vector<Node*> implicit_outputs_;\n};\n\n/// Store data loaded from one dyndep file.  Map from an edge\n/// to its dynamically-discovered dependency information.\n/// This is a struct rather than a typedef so that we can\n/// forward-declare it in other headers.\nstruct DyndepFile: public std::map<Edge*, Dyndeps> {};\n\n/// DyndepLoader loads dynamically discovered dependencies, as\n/// referenced via the \"dyndep\" attribute in build files.\nstruct DyndepLoader {\n  DyndepLoader(State* state, DiskInterface* disk_interface,\n               Explanations* explanations = nullptr)\n      : state_(state), disk_interface_(disk_interface),\n        explanations_(explanations) {}\n\n  /// Load a dyndep file from the given node's path and update the\n  /// build graph with the new information.  One overload accepts\n  /// a caller-owned 'DyndepFile' object in which to store the\n  /// information loaded from the dyndep file.\n  bool LoadDyndeps(Node* node, std::string* err) const;\n  bool LoadDyndeps(Node* node, DyndepFile* ddf, std::string* err) const;\n\n private:\n  bool LoadDyndepFile(Node* file, DyndepFile* ddf, std::string* err) const;\n\n  bool UpdateEdge(Edge* edge, Dyndeps const* dyndeps, std::string* err) const;\n\n  State* state_;\n  DiskInterface* disk_interface_;\n  mutable OptionalExplanations explanations_;\n};\n\n#endif  // NINJA_DYNDEP_LOADER_H_\n"
  },
  {
    "path": "src/dyndep_parser.cc",
    "content": "// Copyright 2015 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"dyndep_parser.h\"\n\n#include <vector>\n\n#include \"dyndep.h\"\n#include \"graph.h\"\n#include \"state.h\"\n#include \"util.h\"\n#include \"version.h\"\n\nusing namespace std;\n\nDyndepParser::DyndepParser(State* state, FileReader* file_reader,\n                           DyndepFile* dyndep_file)\n    : Parser(state, file_reader)\n    , dyndep_file_(dyndep_file) {\n}\n\nbool DyndepParser::Parse(const string& filename, const string& input,\n                         string* err) {\n  lexer_.Start(filename, input);\n\n  // Require a supported ninja_dyndep_version value immediately so\n  // we can exit before encountering any syntactic surprises.\n  bool haveDyndepVersion = false;\n\n  for (;;) {\n    Lexer::Token token = lexer_.ReadToken();\n    switch (token) {\n    case Lexer::BUILD: {\n      if (!haveDyndepVersion)\n        return lexer_.Error(\"expected 'ninja_dyndep_version = ...'\", err);\n      if (!ParseEdge(err))\n        return false;\n      break;\n    }\n    case Lexer::IDENT: {\n      lexer_.UnreadToken();\n      if (haveDyndepVersion)\n        return lexer_.Error(string(\"unexpected \") + Lexer::TokenName(token),\n                            err);\n      if (!ParseDyndepVersion(err))\n        return false;\n      haveDyndepVersion = true;\n      break;\n    }\n    case Lexer::ERROR:\n      return lexer_.Error(lexer_.DescribeLastError(), err);\n    case Lexer::TEOF:\n      if (!haveDyndepVersion)\n        return lexer_.Error(\"expected 'ninja_dyndep_version = ...'\", err);\n      return true;\n    case Lexer::NEWLINE:\n      break;\n    default:\n      return lexer_.Error(string(\"unexpected \") + Lexer::TokenName(token),\n                          err);\n    }\n  }\n  return false;  // not reached\n}\n\nbool DyndepParser::ParseDyndepVersion(string* err) {\n  string name;\n  EvalString let_value;\n  if (!ParseLet(&name, &let_value, err))\n    return false;\n  if (name != \"ninja_dyndep_version\") {\n    return lexer_.Error(\"expected 'ninja_dyndep_version = ...'\", err);\n  }\n  string version = let_value.Evaluate(&env_);\n  int major, minor;\n  ParseVersion(version, &major, &minor);\n  if (major != 1 || minor != 0) {\n    return lexer_.Error(\n      string(\"unsupported 'ninja_dyndep_version = \") + version + \"'\", err);\n  }\n  return true;\n}\n\nbool DyndepParser::ParseLet(string* key, EvalString* value, string* err) {\n  if (!lexer_.ReadIdent(key))\n    return lexer_.Error(\"expected variable name\", err);\n  return (ExpectToken(Lexer::EQUALS, err) && lexer_.ReadVarValue(value, err));\n}\n\nbool DyndepParser::ParseEdge(string* err) {\n  // Parse one explicit output.  We expect it to already have an edge.\n  // We will record its dynamically-discovered dependency information.\n  Dyndeps* dyndeps = NULL;\n  {\n    EvalString out0;\n    if (!lexer_.ReadPath(&out0, err))\n      return false;\n    if (out0.empty())\n      return lexer_.Error(\"expected path\", err);\n\n    string path = out0.Evaluate(&env_);\n    if (path.empty())\n      return lexer_.Error(\"empty path\", err);\n    uint64_t slash_bits;\n    CanonicalizePath(&path, &slash_bits);\n    Node* node = state_->LookupNode(path);\n    if (!node || !node->in_edge())\n      return lexer_.Error(\"no build statement exists for '\" + path + \"'\", err);\n    Edge* edge = node->in_edge();\n    std::pair<DyndepFile::iterator, bool> res =\n      dyndep_file_->insert(DyndepFile::value_type(edge, Dyndeps()));\n    if (!res.second)\n      return lexer_.Error(\"multiple statements for '\" + path + \"'\", err);\n    dyndeps = &res.first->second;\n  }\n\n  // Disallow explicit outputs.\n  {\n    EvalString out;\n    if (!lexer_.ReadPath(&out, err))\n      return false;\n    if (!out.empty())\n      return lexer_.Error(\"explicit outputs not supported\", err);\n  }\n\n  // Parse implicit outputs, if any.\n  vector<EvalString> outs;\n  if (lexer_.PeekToken(Lexer::PIPE)) {\n    for (;;) {\n      EvalString out;\n      if (!lexer_.ReadPath(&out, err))\n        return err;\n      if (out.empty())\n        break;\n      outs.push_back(out);\n    }\n  }\n\n  if (!ExpectToken(Lexer::COLON, err))\n    return false;\n\n  string rule_name;\n  if (!lexer_.ReadIdent(&rule_name) || rule_name != \"dyndep\")\n    return lexer_.Error(\"expected build command name 'dyndep'\", err);\n\n  // Disallow explicit inputs.\n  {\n    EvalString in;\n    if (!lexer_.ReadPath(&in, err))\n      return false;\n    if (!in.empty())\n      return lexer_.Error(\"explicit inputs not supported\", err);\n  }\n\n  // Parse implicit inputs, if any.\n  vector<EvalString> ins;\n  if (lexer_.PeekToken(Lexer::PIPE)) {\n    for (;;) {\n      EvalString in;\n      if (!lexer_.ReadPath(&in, err))\n        return err;\n      if (in.empty())\n        break;\n      ins.push_back(in);\n    }\n  }\n\n  // Disallow order-only inputs.\n  if (lexer_.PeekToken(Lexer::PIPE2))\n    return lexer_.Error(\"order-only inputs not supported\", err);\n\n  if (!ExpectToken(Lexer::NEWLINE, err))\n    return false;\n\n  if (lexer_.PeekToken(Lexer::INDENT)) {\n    string key;\n    EvalString val;\n    if (!ParseLet(&key, &val, err))\n      return false;\n    if (key != \"restat\")\n      return lexer_.Error(\"binding is not 'restat'\", err);\n    string value = val.Evaluate(&env_);\n    dyndeps->restat_ = !value.empty();\n  }\n\n  dyndeps->implicit_inputs_.reserve(ins.size());\n  for (const EvalString& in : ins) {\n    string path = in.Evaluate(&env_);\n    if (path.empty())\n      return lexer_.Error(\"empty path\", err);\n    uint64_t slash_bits;\n    CanonicalizePath(&path, &slash_bits);\n    Node* n = state_->GetNode(path, slash_bits);\n    dyndeps->implicit_inputs_.push_back(n);\n  }\n\n  dyndeps->implicit_outputs_.reserve(outs.size());\n  for (const EvalString& out : outs) {\n    string path = out.Evaluate(&env_);\n    if (path.empty())\n      return lexer_.Error(\"empty path\", err);\n    uint64_t slash_bits;\n    CanonicalizePath(&path, &slash_bits);\n    Node* n = state_->GetNode(path, slash_bits);\n    dyndeps->implicit_outputs_.push_back(n);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "src/dyndep_parser.h",
    "content": "// Copyright 2015 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_DYNDEP_PARSER_H_\n#define NINJA_DYNDEP_PARSER_H_\n\n#include \"eval_env.h\"\n#include \"parser.h\"\n\nstruct DyndepFile;\nstruct EvalString;\n\n/// Parses dyndep files.\nstruct DyndepParser: public Parser {\n  DyndepParser(State* state, FileReader* file_reader,\n               DyndepFile* dyndep_file);\n\n  /// Parse a text string of input.  Used by tests.\n  bool ParseTest(const std::string& input, std::string* err) {\n    return Parse(\"input\", input, err);\n  }\n\nprivate:\n  /// Parse a file, given its contents as a string.\n  bool Parse(const std::string& filename, const std::string& input,\n             std:: string* err);\n\n  bool ParseDyndepVersion(std::string* err);\n  bool ParseLet(std::string* key, EvalString* val, std::string* err);\n  bool ParseEdge(std::string* err);\n\n  DyndepFile* dyndep_file_;\n  BindingEnv env_;\n};\n\n#endif  // NINJA_DYNDEP_PARSER_H_\n"
  },
  {
    "path": "src/dyndep_parser_test.cc",
    "content": "// Copyright 2015 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"dyndep_parser.h\"\n\n#include <map>\n#include <vector>\n\n#include \"dyndep.h\"\n#include \"graph.h\"\n#include \"state.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nstruct DyndepParserTest : public testing::Test {\n  void AssertParse(const char* input) {\n    DyndepParser parser(&state_, &fs_, &dyndep_file_);\n    string err;\n    EXPECT_TRUE(parser.ParseTest(input, &err));\n    ASSERT_EQ(\"\", err);\n  }\n\n  virtual void SetUp() {\n    ::AssertParse(&state_,\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"build out otherout: touch\\n\");\n  }\n\n  State state_;\n  VirtualFileSystem fs_;\n  DyndepFile dyndep_file_;\n};\n\nTEST_F(DyndepParserTest, Empty) {\n  const char kInput[] =\n\"\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: expected 'ninja_dyndep_version = ...'\\n\", err);\n}\n\nTEST_F(DyndepParserTest, Version1) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"));\n}\n\nTEST_F(DyndepParserTest, Version1Extra) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1-extra\\n\"));\n}\n\nTEST_F(DyndepParserTest, Version1_0) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1.0\\n\"));\n}\n\nTEST_F(DyndepParserTest, Version1_0Extra) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1.0-extra\\n\"));\n}\n\nTEST_F(DyndepParserTest, CommentVersion) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"# comment\\n\"\n\"ninja_dyndep_version = 1\\n\"));\n}\n\nTEST_F(DyndepParserTest, BlankLineVersion) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"\\n\"\n\"ninja_dyndep_version = 1\\n\"));\n}\n\nTEST_F(DyndepParserTest, VersionCRLF) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\r\\n\"));\n}\n\nTEST_F(DyndepParserTest, CommentVersionCRLF) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"# comment\\r\\n\"\n\"ninja_dyndep_version = 1\\r\\n\"));\n}\n\nTEST_F(DyndepParserTest, BlankLineVersionCRLF) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"\\r\\n\"\n\"ninja_dyndep_version = 1\\r\\n\"));\n}\n\nTEST_F(DyndepParserTest, VersionUnexpectedEOF) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1.0\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: unexpected EOF\\n\"\n            \"ninja_dyndep_version = 1.0\\n\"\n            \"                          ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, UnsupportedVersion0) {\n  const char kInput[] =\n\"ninja_dyndep_version = 0\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: unsupported 'ninja_dyndep_version = 0'\\n\"\n            \"ninja_dyndep_version = 0\\n\"\n            \"                        ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, UnsupportedVersion1_1) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1.1\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: unsupported 'ninja_dyndep_version = 1.1'\\n\"\n            \"ninja_dyndep_version = 1.1\\n\"\n            \"                          ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, DuplicateVersion) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"ninja_dyndep_version = 1\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: unexpected identifier\\n\", err);\n}\n\nTEST_F(DyndepParserTest, MissingVersionOtherVar) {\n  const char kInput[] =\n\"not_ninja_dyndep_version = 1\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: expected 'ninja_dyndep_version = ...'\\n\"\n            \"not_ninja_dyndep_version = 1\\n\"\n            \"                            ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, MissingVersionBuild) {\n  const char kInput[] =\n\"build out: dyndep\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: expected 'ninja_dyndep_version = ...'\\n\", err);\n}\n\nTEST_F(DyndepParserTest, UnexpectedEqual) {\n  const char kInput[] =\n\"= 1\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: unexpected '='\\n\", err);\n}\n\nTEST_F(DyndepParserTest, UnexpectedIndent) {\n  const char kInput[] =\n\" = 1\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:1: unexpected indent\\n\", err);\n}\n\nTEST_F(DyndepParserTest, OutDuplicate) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"build out: dyndep\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:3: multiple statements for 'out'\\n\"\n            \"build out: dyndep\\n\"\n            \"         ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, OutDuplicateThroughOther) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"build otherout: dyndep\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:3: multiple statements for 'otherout'\\n\"\n            \"build otherout: dyndep\\n\"\n            \"              ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, NoOutEOF) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: unexpected EOF\\n\"\n            \"build\\n\"\n            \"     ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, NoOutColon) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build :\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: expected path\\n\"\n            \"build :\\n\"\n            \"      ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, OutNoStatement) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build missing: dyndep\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: no build statement exists for 'missing'\\n\"\n            \"build missing: dyndep\\n\"\n            \"             ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, OutEOF) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: unexpected EOF\\n\"\n            \"build out\\n\"\n            \"         ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, OutNoRule) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out:\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: expected build command name 'dyndep'\\n\"\n            \"build out:\\n\"\n            \"          ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, OutBadRule) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: touch\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: expected build command name 'dyndep'\\n\"\n            \"build out: touch\\n\"\n            \"           ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, BuildEOF) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: unexpected EOF\\n\"\n            \"build out: dyndep\\n\"\n            \"                 ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, ExplicitOut) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out exp: dyndep\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: explicit outputs not supported\\n\"\n            \"build out exp: dyndep\\n\"\n            \"             ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, ExplicitIn) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep exp\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: explicit inputs not supported\\n\"\n            \"build out: dyndep exp\\n\"\n            \"                     ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, OrderOnlyIn) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep ||\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:2: order-only inputs not supported\\n\"\n            \"build out: dyndep ||\\n\"\n            \"                  ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, BadBinding) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"  not_restat = 1\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:3: binding is not 'restat'\\n\"\n            \"  not_restat = 1\\n\"\n            \"                ^ near here\", err);\n}\n\nTEST_F(DyndepParserTest, RestatTwice) {\n  const char kInput[] =\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"  restat = 1\\n\"\n\"  restat = 1\\n\";\n  DyndepParser parser(&state_, &fs_, &dyndep_file_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:4: unexpected indent\\n\", err);\n}\n\nTEST_F(DyndepParserTest, NoImplicit) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n  EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n}\n\nTEST_F(DyndepParserTest, EmptyImplicit) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out | : dyndep |\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n  EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n}\n\nTEST_F(DyndepParserTest, ImplicitIn) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | impin\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n  ASSERT_EQ(1u, i->second.implicit_inputs_.size());\n  EXPECT_EQ(\"impin\", i->second.implicit_inputs_[0]->path());\n}\n\nTEST_F(DyndepParserTest, ImplicitIns) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | impin1 impin2\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n  ASSERT_EQ(2u, i->second.implicit_inputs_.size());\n  EXPECT_EQ(\"impin1\", i->second.implicit_inputs_[0]->path());\n  EXPECT_EQ(\"impin2\", i->second.implicit_inputs_[1]->path());\n}\n\nTEST_F(DyndepParserTest, ImplicitOut) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out | impout: dyndep\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  ASSERT_EQ(1u, i->second.implicit_outputs_.size());\n  EXPECT_EQ(\"impout\", i->second.implicit_outputs_[0]->path());\n  EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n}\n\nTEST_F(DyndepParserTest, ImplicitOuts) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out | impout1 impout2 : dyndep\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  ASSERT_EQ(2u, i->second.implicit_outputs_.size());\n  EXPECT_EQ(\"impout1\", i->second.implicit_outputs_[0]->path());\n  EXPECT_EQ(\"impout2\", i->second.implicit_outputs_[1]->path());\n  EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n}\n\nTEST_F(DyndepParserTest, ImplicitInsAndOuts) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out | impout1 impout2: dyndep | impin1 impin2\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  ASSERT_EQ(2u, i->second.implicit_outputs_.size());\n  EXPECT_EQ(\"impout1\", i->second.implicit_outputs_[0]->path());\n  EXPECT_EQ(\"impout2\", i->second.implicit_outputs_[1]->path());\n  ASSERT_EQ(2u, i->second.implicit_inputs_.size());\n  EXPECT_EQ(\"impin1\", i->second.implicit_inputs_[0]->path());\n  EXPECT_EQ(\"impin2\", i->second.implicit_inputs_[1]->path());\n}\n\nTEST_F(DyndepParserTest, Restat) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"  restat = 1\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(true, i->second.restat_);\n  EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n  EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n}\n\nTEST_F(DyndepParserTest, OtherOutput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build otherout: dyndep\\n\"));\n\n  EXPECT_EQ(1u, dyndep_file_.size());\n  DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n  ASSERT_NE(i, dyndep_file_.end());\n  EXPECT_EQ(false, i->second.restat_);\n  EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n  EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n}\n\nTEST_F(DyndepParserTest, MultipleEdges) {\n    ::AssertParse(&state_,\n\"build out2: touch\\n\");\n  ASSERT_EQ(2u, state_.edges_.size());\n  ASSERT_EQ(1u, state_.edges_[1]->outputs_.size());\n  EXPECT_EQ(\"out2\", state_.edges_[1]->outputs_[0]->path());\n  EXPECT_EQ(0u, state_.edges_[0]->inputs_.size());\n\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"build out2: dyndep\\n\"\n\"  restat = 1\\n\"));\n\n  EXPECT_EQ(2u, dyndep_file_.size());\n  {\n    DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);\n    ASSERT_NE(i, dyndep_file_.end());\n    EXPECT_EQ(false, i->second.restat_);\n    EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n    EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n  }\n  {\n    DyndepFile::iterator i = dyndep_file_.find(state_.edges_[1]);\n    ASSERT_NE(i, dyndep_file_.end());\n    EXPECT_EQ(true, i->second.restat_);\n    EXPECT_EQ(0u, i->second.implicit_outputs_.size());\n    EXPECT_EQ(0u, i->second.implicit_inputs_.size());\n  }\n}\n"
  },
  {
    "path": "src/edit_distance.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"edit_distance.h\"\n\n#include <algorithm>\n#include <vector>\n\nint EditDistance(const StringPiece& s1,\n                 const StringPiece& s2,\n                 bool allow_replacements,\n                 int max_edit_distance) {\n  // The algorithm implemented below is the \"classic\"\n  // dynamic-programming algorithm for computing the Levenshtein\n  // distance, which is described here:\n  //\n  //   http://en.wikipedia.org/wiki/Levenshtein_distance\n  //\n  // Although the algorithm is typically described using an m x n\n  // array, only one row plus one element are used at a time, so this\n  // implementation just keeps one vector for the row.  To update one entry,\n  // only the entries to the left, top, and top-left are needed.  The left\n  // entry is in row[x-1], the top entry is what's in row[x] from the last\n  // iteration, and the top-left entry is stored in previous.\n  int m = static_cast<int>(s1.len_);\n  int n = static_cast<int>(s2.len_);\n\n  std::vector<int> row(n + 1);\n  for (int i = 1; i <= n; ++i)\n    row[i] = i;\n\n  for (int y = 1; y <= m; ++y) {\n    row[0] = y;\n    int best_this_row = row[0];\n\n    int previous = y - 1;\n    for (int x = 1; x <= n; ++x) {\n      int old_row = row[x];\n      if (allow_replacements) {\n        row[x] = std::min(previous + (s1.str_[y - 1] == s2.str_[x - 1] ? 0 : 1),\n                          std::min(row[x - 1], row[x]) + 1);\n      }\n      else {\n        if (s1.str_[y - 1] == s2.str_[x - 1])\n          row[x] = previous;\n        else\n          row[x] = std::min(row[x - 1], row[x]) + 1;\n      }\n      previous = old_row;\n      best_this_row = std::min(best_this_row, row[x]);\n    }\n\n    if (max_edit_distance && best_this_row > max_edit_distance)\n      return max_edit_distance + 1;\n  }\n\n  return row[n];\n}\n"
  },
  {
    "path": "src/edit_distance.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_EDIT_DISTANCE_H_\n#define NINJA_EDIT_DISTANCE_H_\n\n#include \"string_piece.h\"\n\nint EditDistance(const StringPiece& s1,\n                 const StringPiece& s2,\n                 bool allow_replacements = true,\n                 int max_edit_distance = 0);\n\n#endif  // NINJA_EDIT_DISTANCE_H_\n"
  },
  {
    "path": "src/edit_distance_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"edit_distance.h\"\n\n#include \"test.h\"\n\nTEST(EditDistanceTest, TestEmpty) {\n  EXPECT_EQ(5, EditDistance(\"\", \"ninja\"));\n  EXPECT_EQ(5, EditDistance(\"ninja\", \"\"));\n  EXPECT_EQ(0, EditDistance(\"\", \"\"));\n}\n\nTEST(EditDistanceTest, TestMaxDistance) {\n  const bool allow_replacements = true;\n  for (int max_distance = 1; max_distance < 7; ++max_distance) {\n    EXPECT_EQ(max_distance + 1,\n              EditDistance(\"abcdefghijklmnop\", \"ponmlkjihgfedcba\",\n                           allow_replacements, max_distance));\n  }\n}\n\nTEST(EditDistanceTest, TestAllowReplacements) {\n  bool allow_replacements = true;\n  EXPECT_EQ(1, EditDistance(\"ninja\", \"njnja\", allow_replacements));\n  EXPECT_EQ(1, EditDistance(\"njnja\", \"ninja\", allow_replacements));\n\n  allow_replacements = false;\n  EXPECT_EQ(2, EditDistance(\"ninja\", \"njnja\", allow_replacements));\n  EXPECT_EQ(2, EditDistance(\"njnja\", \"ninja\", allow_replacements));\n}\n\nTEST(EditDistanceTest, TestBasics) {\n  EXPECT_EQ(0, EditDistance(\"browser_tests\", \"browser_tests\"));\n  EXPECT_EQ(1, EditDistance(\"browser_test\", \"browser_tests\"));\n  EXPECT_EQ(1, EditDistance(\"browser_tests\", \"browser_test\"));\n}\n"
  },
  {
    "path": "src/elide_middle.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"elide_middle.h\"\n\n#include <assert.h>\n#include <string.h>\n\n// Convenience class used to iterate over the ANSI color sequences\n// of an input string. Note that this ignores non-color related\n// ANSI sequences. Usage is:\n//\n//  - Create instance, passing the input string to the constructor.\n//  - Loop over each sequence with:\n//\n//        AnsiColorSequenceIterator iter;\n//        while (iter.HasSequence()) {\n//          .. use iter.SequenceStart() and iter.SequenceEnd()\n//          iter.NextSequence();\n//        }\n//\nstruct AnsiColorSequenceIterator {\n  // Constructor takes input string .\n  AnsiColorSequenceIterator(const std::string& input)\n      : input_(input.data()), input_end_(input_ + input.size()) {\n    FindNextSequenceFrom(input_);\n  }\n\n  // Return true if an ANSI sequence was found.\n  bool HasSequence() const { return cur_end_ != 0; }\n\n  // Start of the current sequence.\n  size_t SequenceStart() const { return cur_start_; }\n\n  // End of the current sequence (index of the first character\n  // following the sequence).\n  size_t SequenceEnd() const { return cur_end_; }\n\n  // Size of the current sequence in characters.\n  size_t SequenceSize() const { return cur_end_ - cur_start_; }\n\n  // Returns true if |input_index| belongs to the current sequence.\n  bool SequenceContains(size_t input_index) const {\n    return (input_index >= cur_start_ && input_index < cur_end_);\n  }\n\n  // Find the next sequence, if any, from the input.\n  // Returns false is there is no more sequence.\n  bool NextSequence() {\n    if (FindNextSequenceFrom(input_ + cur_end_))\n      return true;\n\n    cur_start_ = 0;\n    cur_end_ = 0;\n    return false;\n  }\n\n  // Reset iterator to start of input.\n  void Reset() {\n    cur_start_ = cur_end_ = 0;\n    FindNextSequenceFrom(input_);\n  }\n\n private:\n  // Find the next sequence from the input, |from| being the starting position\n  // for the search, and must be in the [input_, input_end_] interval. On\n  // success, returns true after setting cur_start_ and cur_end_, on failure,\n  // return false.\n  bool FindNextSequenceFrom(const char* from) {\n    assert(from >= input_ && from <= input_end_);\n    auto* seq =\n        static_cast<const char*>(::memchr(from, '\\x1b', input_end_ - from));\n    if (!seq)\n      return false;\n\n    // The smallest possible color sequence if '\\x1c[0m` and has four\n    // characters.\n    if (seq + 4 > input_end_)\n      return false;\n\n    if (seq[1] != '[')\n      return FindNextSequenceFrom(seq + 1);\n\n    // Skip parameters (digits + ; separator)\n    auto is_parameter_char = [](char ch) -> bool {\n      return (ch >= '0' && ch <= '9') || ch == ';';\n    };\n\n    const char* end = seq + 2;\n    while (is_parameter_char(end[0])) {\n      if (++end == input_end_)\n        return false;  // Incomplete sequence (no command).\n    }\n\n    if (*end++ != 'm') {\n      // Not a color sequence. Restart the search after the first\n      // character following the [, in case this was a 3-char ANSI\n      // sequence (which is ignored here).\n      return FindNextSequenceFrom(seq + 3);\n    }\n\n    // Found it!\n    cur_start_ = seq - input_;\n    cur_end_ = end - input_;\n    return true;\n  }\n\n  size_t cur_start_ = 0;\n  size_t cur_end_ = 0;\n  const char* input_;\n  const char* input_end_;\n};\n\n// A class used to iterate over all characters of an input string,\n// and return its visible position in the terminal, and whether that\n// specific character is visible (or otherwise part of an ANSI color sequence).\n//\n// Example sequence and iterations, where 'ANSI' represents an ANSI Color\n// sequence, and | is used to express concatenation\n//\n//   |abcd|ANSI|efgh|ANSI|ijk|      input string\n//\n//                11 1111 111\n//    0123 4567 8901 2345 678       input indices\n//\n//                          1\n//    0123 4444 4567 8888 890       visible positions\n//\n//    TTTT FFFF TTTT FFFF TTT       is_visible\n//\n// Usage is:\n//\n//     VisibleInputCharsIterator iter(input);\n//     while (iter.HasChar()) {\n//       ... use iter.InputIndex() to get input index of current char.\n//       ... use iter.VisiblePosition() to get its visible position.\n//       ... use iter.IsVisible() to check whether the current char is visible.\n//\n//       NextChar();\n//     }\n//\nstruct VisibleInputCharsIterator {\n  VisibleInputCharsIterator(const std::string& input)\n      : input_size_(input.size()), ansi_iter_(input) {}\n\n  // Return true if there is a character in the sequence.\n  bool HasChar() const { return input_index_ < input_size_; }\n\n  // Return current input index.\n  size_t InputIndex() const { return input_index_; }\n\n  // Return current visible position.\n  size_t VisiblePosition() const { return visible_pos_; }\n\n  // Return true if the current input character is visible\n  // (i.e. not part of an ANSI color sequence).\n  bool IsVisible() const { return !ansi_iter_.SequenceContains(input_index_); }\n\n  // Find next character from the input.\n  void NextChar() {\n    visible_pos_ += IsVisible();\n    if (++input_index_ == ansi_iter_.SequenceEnd()) {\n      ansi_iter_.NextSequence();\n    }\n  }\n\n private:\n  size_t input_size_;\n  size_t input_index_ = 0;\n  size_t visible_pos_ = 0;\n  AnsiColorSequenceIterator ansi_iter_;\n};\n\nvoid ElideMiddleInPlace(std::string& str, size_t max_width) {\n  if (str.size() <= max_width) {\n    return;\n  }\n  // Look for an ESC character. If there is none, use a fast path\n  // that avoids any intermediate allocations.\n  if (str.find('\\x1b') == std::string::npos) {\n    const int ellipsis_width = 3;  // Space for \"...\".\n\n    // If max width is too small, do not keep anything from the input.\n    if (max_width <= ellipsis_width) {\n      str.assign(\"...\", max_width);\n      return;\n    }\n\n    // Keep only |max_width - ellipsis_size| visible characters from the input\n    // which will be split into two spans separated by \"...\".\n    const size_t remaining_size = max_width - ellipsis_width;\n    const size_t left_span_size = remaining_size / 2;\n    const size_t right_span_size = remaining_size - left_span_size;\n\n    // Replace the gap in the input between the spans with \"...\"\n    const size_t gap_start = left_span_size;\n    const size_t gap_end = str.size() - right_span_size;\n    str.replace(gap_start, gap_end - gap_start, \"...\");\n    return;\n  }\n\n  // Compute visible width.\n  size_t visible_width = str.size();\n  for (AnsiColorSequenceIterator ansi(str); ansi.HasSequence();\n       ansi.NextSequence()) {\n    visible_width -= ansi.SequenceSize();\n  }\n\n  if (visible_width <= max_width)\n    return;\n\n  // Compute the widths of the ellipsis, left span and right span\n  // visible space.\n  const size_t ellipsis_width = max_width < 3 ? max_width : 3;\n  const size_t visible_left_span_size = (max_width - ellipsis_width) / 2;\n  const size_t visible_right_span_size =\n      (max_width - ellipsis_width) - visible_left_span_size;\n\n  // Compute the gap of visible characters that will be replaced by\n  // the ellipsis in visible space.\n  const size_t visible_gap_start = visible_left_span_size;\n  const size_t visible_gap_end = visible_width - visible_right_span_size;\n\n  std::string result;\n  result.reserve(str.size());\n\n  // Parse the input chars info to:\n  //\n  // 1) Append any characters belonging to the left span (visible or not).\n  //\n  // 2) Add the ellipsis (\"...\" truncated to ellipsis_width).\n  //    Note that its color is inherited from the left span chars\n  //    which will never end with an ANSI sequence.\n  //\n  // 3) Append any ANSI sequence that appears inside the gap. This\n  //    ensures the characters after the ellipsis appear with\n  //    the right color,\n  //\n  // 4) Append any remaining characters (visible or not) to the result.\n  //\n  VisibleInputCharsIterator iter(str);\n\n  // Step 1 - determine left span length in input chars.\n  for (; iter.HasChar(); iter.NextChar()) {\n    if (iter.VisiblePosition() == visible_gap_start)\n      break;\n  }\n  result.append(str.begin(), str.begin() + iter.InputIndex());\n\n  // Step 2 - Append the possibly-truncated ellipsis.\n  result.append(\"...\", ellipsis_width);\n\n  // Step 3 - Append elided ANSI sequences to the result.\n  for (; iter.HasChar(); iter.NextChar()) {\n    if (iter.VisiblePosition() == visible_gap_end)\n      break;\n    if (!iter.IsVisible())\n      result.push_back(str[iter.InputIndex()]);\n  }\n\n  // Step 4 - Append anything else.\n  result.append(str.begin() + iter.InputIndex(), str.end());\n\n  str = std::move(result);\n}\n"
  },
  {
    "path": "src/elide_middle.h",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_ELIDE_MIDDLE_H_\n#define NINJA_ELIDE_MIDDLE_H_\n\n#include <cstddef>\n#include <string>\n\n/// Elide the given string @a str with '...' in the middle if the length\n/// exceeds @a max_width. Note that this handles ANSI color sequences\n/// properly (non-color related sequences are ignored, but using them\n/// would wreak the cursor position or terminal state anyway).\nvoid ElideMiddleInPlace(std::string& str, size_t max_width);\n\n#endif  // NINJA_ELIDE_MIDDLE_H_\n"
  },
  {
    "path": "src/elide_middle_perftest.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <vector>\n\n#include \"elide_middle.h\"\n#include \"metrics.h\"\n\nstatic const char* kTestInputs[] = {\n  \"01234567890123456789\",\n  \"012345\\x1B[0;35m67890123456789\",\n  \"abcd\\x1b[1;31mefg\\x1b[0mhlkmnopqrstuvwxyz\",\n};\n\nint main() {\n  std::vector<int> times;\n\n  int64_t kMaxTimeMillis = 5 * 1000;\n  int64_t base_time = GetTimeMillis();\n\n  const int kRuns = 100;\n  for (int j = 0; j < kRuns; ++j) {\n    int64_t start = GetTimeMillis();\n    if (start >= base_time + kMaxTimeMillis)\n      break;\n\n    const int kNumRepetitions = 2000;\n    for (int count = kNumRepetitions; count > 0; --count) {\n      for (const char* input : kTestInputs) {\n        size_t input_len = ::strlen(input);\n        for (size_t max_width = input_len; max_width > 0; --max_width) {\n          std::string str(input, input_len);\n          ElideMiddleInPlace(str, max_width);\n        }\n      }\n    }\n\n    int delta = (int)(GetTimeMillis() - start);\n    times.push_back(delta);\n  }\n\n  int min = times[0];\n  int max = times[0];\n  float total = 0;\n  for (size_t i = 0; i < times.size(); ++i) {\n    total += times[i];\n    if (times[i] < min)\n      min = times[i];\n    else if (times[i] > max)\n      max = times[i];\n  }\n\n  printf(\"min %dms  max %dms  avg %.1fms\\n\", min, max, total / times.size());\n\n  return 0;\n}\n"
  },
  {
    "path": "src/elide_middle_test.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"elide_middle.h\"\n\n#include \"test.h\"\n\nnamespace {\n\nstd::string ElideMiddle(const std::string& str, size_t width) {\n  std::string result = str;\n  ElideMiddleInPlace(result, width);\n  return result;\n}\n\n}  // namespace\n\n\nTEST(ElideMiddle, NothingToElide) {\n  std::string input = \"Nothing to elide in this short string.\";\n  EXPECT_EQ(input, ElideMiddle(input, 80));\n  EXPECT_EQ(input, ElideMiddle(input, 38));\n  EXPECT_EQ(\"\", ElideMiddle(input, 0));\n  EXPECT_EQ(\".\", ElideMiddle(input, 1));\n  EXPECT_EQ(\"..\", ElideMiddle(input, 2));\n  EXPECT_EQ(\"...\", ElideMiddle(input, 3));\n}\n\nTEST(ElideMiddle, ElideInTheMiddle) {\n  std::string input = \"01234567890123456789\";\n  EXPECT_EQ(\"...9\", ElideMiddle(input, 4));\n  EXPECT_EQ(\"0...9\", ElideMiddle(input, 5));\n  EXPECT_EQ(\"012...789\", ElideMiddle(input, 9));\n  EXPECT_EQ(\"012...6789\", ElideMiddle(input, 10));\n  EXPECT_EQ(\"0123...6789\", ElideMiddle(input, 11));\n  EXPECT_EQ(\"01234567...23456789\", ElideMiddle(input, 19));\n  EXPECT_EQ(\"01234567890123456789\", ElideMiddle(input, 20));\n}\n\n// A few ANSI escape sequences. These macros make the following\n// test easier to read and understand.\n#define MAGENTA \"\\x1B[0;35m\"\n#define NOTHING \"\\33[m\"\n#define RED \"\\x1b[1;31m\"\n#define RESET \"\\x1b[0m\"\n\nTEST(ElideMiddle, ElideAnsiEscapeCodes) {\n  std::string input = \"012345\" MAGENTA \"67890123456789\";\n  EXPECT_EQ(\"012...\" MAGENTA \"6789\", ElideMiddle(input, 10));\n  EXPECT_EQ(\"012345\" MAGENTA \"67...23456789\", ElideMiddle(input, 19));\n\n  EXPECT_EQ(\"Nothing \" NOTHING \" string.\",\n            ElideMiddle(\"Nothing \" NOTHING \" string.\", 18));\n  EXPECT_EQ(\"0\" NOTHING \"12...6789\",\n            ElideMiddle(\"0\" NOTHING \"1234567890123456789\", 10));\n\n  input = \"abcd\" RED \"efg\" RESET \"hlkmnopqrstuvwxyz\";\n  EXPECT_EQ(\"\" RED RESET, ElideMiddle(input, 0));\n  EXPECT_EQ(\".\" RED RESET, ElideMiddle(input, 1));\n  EXPECT_EQ(\"..\" RED RESET, ElideMiddle(input, 2));\n  EXPECT_EQ(\"...\" RED RESET, ElideMiddle(input, 3));\n  EXPECT_EQ(\"...\" RED RESET \"z\", ElideMiddle(input, 4));\n  EXPECT_EQ(\"a...\" RED RESET \"z\", ElideMiddle(input, 5));\n  EXPECT_EQ(\"a...\" RED RESET \"yz\", ElideMiddle(input, 6));\n  EXPECT_EQ(\"ab...\" RED RESET \"yz\", ElideMiddle(input, 7));\n  EXPECT_EQ(\"ab...\" RED RESET \"xyz\", ElideMiddle(input, 8));\n  EXPECT_EQ(\"abc...\" RED RESET \"xyz\", ElideMiddle(input, 9));\n  EXPECT_EQ(\"abc...\" RED RESET \"wxyz\", ElideMiddle(input, 10));\n  EXPECT_EQ(\"abcd...\" RED RESET \"wxyz\", ElideMiddle(input, 11));\n  EXPECT_EQ(\"abcd...\" RED RESET \"vwxyz\", ElideMiddle(input, 12));\n\n  EXPECT_EQ(\"abcd\" RED \"ef...\" RESET \"uvwxyz\", ElideMiddle(input, 15));\n  EXPECT_EQ(\"abcd\" RED \"ef...\" RESET \"tuvwxyz\", ElideMiddle(input, 16));\n  EXPECT_EQ(\"abcd\" RED \"efg...\" RESET \"tuvwxyz\", ElideMiddle(input, 17));\n  EXPECT_EQ(\"abcd\" RED \"efg...\" RESET \"stuvwxyz\", ElideMiddle(input, 18));\n  EXPECT_EQ(\"abcd\" RED \"efg\" RESET \"h...stuvwxyz\", ElideMiddle(input, 19));\n\n  input = \"abcdef\" RED \"A\" RESET \"BC\";\n  EXPECT_EQ(\"...\" RED RESET \"C\", ElideMiddle(input, 4));\n  EXPECT_EQ(\"a...\" RED RESET \"C\", ElideMiddle(input, 5));\n  EXPECT_EQ(\"a...\" RED RESET \"BC\", ElideMiddle(input, 6));\n  EXPECT_EQ(\"ab...\" RED RESET \"BC\", ElideMiddle(input, 7));\n  EXPECT_EQ(\"ab...\" RED \"A\" RESET \"BC\", ElideMiddle(input, 8));\n  EXPECT_EQ(\"abcdef\" RED \"A\" RESET \"BC\", ElideMiddle(input, 9));\n}\n\n#undef RESET\n#undef RED\n#undef NOTHING\n#undef MAGENTA\n"
  },
  {
    "path": "src/eval_env.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <assert.h>\n\n#include \"eval_env.h\"\n\nusing namespace std;\n\nstring BindingEnv::LookupVariable(const string& var) {\n  map<string, string>::iterator i = bindings_.find(var);\n  if (i != bindings_.end())\n    return i->second;\n  if (parent_)\n    return parent_->LookupVariable(var);\n  return \"\";\n}\n\nvoid BindingEnv::AddBinding(const string& key, const string& val) {\n  bindings_[key] = val;\n}\n\nvoid BindingEnv::AddRule(std::unique_ptr<const Rule> rule) {\n  assert(LookupRuleCurrentScope(rule->name()) == NULL);\n  rules_[rule->name()] = std::move(rule);\n}\n\nconst Rule* BindingEnv::LookupRuleCurrentScope(const string& rule_name) {\n  auto i = rules_.find(rule_name);\n  if (i == rules_.end())\n    return NULL;\n  return i->second.get();\n}\n\nconst Rule* BindingEnv::LookupRule(const string& rule_name) {\n  auto i = rules_.find(rule_name);\n  if (i != rules_.end())\n    return i->second.get();\n  if (parent_)\n    return parent_->LookupRule(rule_name);\n  return NULL;\n}\n\nvoid Rule::AddBinding(const string& key, const EvalString& val) {\n  bindings_[key] = val;\n}\n\nconst EvalString* Rule::GetBinding(const string& key) const {\n  Bindings::const_iterator i = bindings_.find(key);\n  if (i == bindings_.end())\n    return NULL;\n  return &i->second;\n}\n\nstd::unique_ptr<Rule> Rule::Phony() {\n  auto rule = std::unique_ptr<Rule>(new Rule(\"phony\"));\n  rule->phony_ = true;\n  return rule;\n}\n\nbool Rule::IsPhony() const {\n  return phony_;\n}\n\n// static\nbool Rule::IsReservedBinding(const string& var) {\n  return var == \"command\" ||\n      var == \"depfile\" ||\n      var == \"dyndep\" ||\n      var == \"description\" ||\n      var == \"deps\" ||\n      var == \"generator\" ||\n      var == \"pool\" ||\n      var == \"restat\" ||\n      var == \"rspfile\" ||\n      var == \"rspfile_content\" ||\n      var == \"msvc_deps_prefix\";\n}\n\nconst map<string, std::unique_ptr<const Rule>>& BindingEnv::GetRules() const {\n  return rules_;\n}\n\nstring BindingEnv::LookupWithFallback(const string& var,\n                                      const EvalString* eval,\n                                      Env* env) {\n  map<string, string>::iterator i = bindings_.find(var);\n  if (i != bindings_.end())\n    return i->second;\n\n  if (eval)\n    return eval->Evaluate(env);\n\n  if (parent_)\n    return parent_->LookupVariable(var);\n\n  return \"\";\n}\n\nstring EvalString::Evaluate(Env* env) const {\n  if (parsed_.empty()) {\n    return single_token_;\n  }\n\n  string result;\n  for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) {\n    if (i->second == RAW)\n      result.append(i->first);\n    else\n      result.append(env->LookupVariable(i->first));\n  }\n  return result;\n}\n\nvoid EvalString::AddText(StringPiece text) {\n  if (parsed_.empty()) {\n    single_token_.append(text.begin(), text.end());\n  } else if (!parsed_.empty() && parsed_.back().second == RAW) {\n    parsed_.back().first.append(text.begin(), text.end());\n  } else {\n    parsed_.push_back(std::make_pair(text.AsString(), RAW));\n  }\n}\n\nvoid EvalString::AddSpecial(StringPiece text) {\n  if (parsed_.empty() && !single_token_.empty()) {\n    // Going from one to two tokens, so we can no longer apply\n    // our single_token_ optimization and need to push everything\n    // onto the vector.\n    parsed_.push_back(std::make_pair(std::move(single_token_), RAW));\n  }\n  parsed_.push_back(std::make_pair(text.AsString(), SPECIAL));\n}\n\nstring EvalString::Serialize() const {\n  string result;\n  if (parsed_.empty() && !single_token_.empty()) {\n    result.append(\"[\");\n    result.append(single_token_);\n    result.append(\"]\");\n  } else {\n    for (const auto& pair : parsed_) {\n      result.append(\"[\");\n      if (pair.second == SPECIAL)\n        result.append(\"$\");\n      result.append(pair.first.begin(), pair.first.end());\n      result.append(\"]\");\n    }\n  }\n  return result;\n}\n\nstring EvalString::Unparse() const {\n  string result;\n  if (parsed_.empty() && !single_token_.empty()) {\n    result.append(single_token_.begin(), single_token_.end());\n  } else {\n    for (TokenList::const_iterator i = parsed_.begin();\n         i != parsed_.end(); ++i) {\n      bool special = (i->second == SPECIAL);\n      if (special)\n        result.append(\"${\");\n      result.append(i->first.begin(), i->first.end());\n      if (special)\n        result.append(\"}\");\n    }\n  }\n  return result;\n}\n"
  },
  {
    "path": "src/eval_env.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_EVAL_ENV_H_\n#define NINJA_EVAL_ENV_H_\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"string_piece.h\"\n\nstruct Rule;\n\n/// An interface for a scope for variable (e.g. \"$foo\") lookups.\nstruct Env {\n  virtual ~Env() {}\n  virtual std::string LookupVariable(const std::string& var) = 0;\n};\n\n/// A tokenized string that contains variable references.\n/// Can be evaluated relative to an Env.\nstruct EvalString {\n  /// @return The evaluated string with variable expanded using value found in\n  ///         environment @a env.\n  std::string Evaluate(Env* env) const;\n\n  /// @return The string with variables not expanded.\n  std::string Unparse() const;\n\n  void Clear() { parsed_.clear(); single_token_.clear(); }\n  bool empty() const { return parsed_.empty() && single_token_.empty(); }\n\n  void AddText(StringPiece text);\n  void AddSpecial(StringPiece text);\n\n  /// Construct a human-readable representation of the parsed state,\n  /// for use in tests.\n  std::string Serialize() const;\n\nprivate:\n  enum TokenType { RAW, SPECIAL };\n  typedef std::vector<std::pair<std::string, TokenType> > TokenList;\n  TokenList parsed_;\n\n  // If we hold only a single RAW token, then we keep it here instead of\n  // pushing it on TokenList. This saves a bunch of allocations for\n  // what is a common case. If parsed_ is nonempty, then this value\n  // must be ignored.\n  std::string single_token_;\n};\n\n/// An invocable build command and associated metadata (description, etc.).\nstruct Rule {\n  explicit Rule(const std::string& name) : name_(name) {}\n\n  static std::unique_ptr<Rule> Phony();\n\n  bool IsPhony() const;\n\n  const std::string& name() const { return name_; }\n\n  void AddBinding(const std::string& key, const EvalString& val);\n\n  static bool IsReservedBinding(const std::string& var);\n\n  const EvalString* GetBinding(const std::string& key) const;\n\n private:\n  // Allow the parsers to reach into this object and fill out its fields.\n  friend struct ManifestParser;\n\n  std::string name_;\n  typedef std::map<std::string, EvalString> Bindings;\n  Bindings bindings_;\n  bool phony_ = false;\n};\n\n/// An Env which contains a mapping of variables to values\n/// as well as a pointer to a parent scope.\nstruct BindingEnv : public Env {\n  BindingEnv() : parent_(NULL) {}\n  explicit BindingEnv(BindingEnv* parent) : parent_(parent) {}\n\n  virtual ~BindingEnv() {}\n  virtual std::string LookupVariable(const std::string& var);\n\n  void AddRule(std::unique_ptr<const Rule> rule);\n  const Rule* LookupRule(const std::string& rule_name);\n  const Rule* LookupRuleCurrentScope(const std::string& rule_name);\n  const std::map<std::string, std::unique_ptr<const Rule>>& GetRules() const;\n\n  void AddBinding(const std::string& key, const std::string& val);\n\n  /// This is tricky.  Edges want lookup scope to go in this order:\n  /// 1) value set on edge itself (edge_->env_)\n  /// 2) value set on rule, with expansion in the edge's scope\n  /// 3) value set on enclosing scope of edge (edge_->env_->parent_)\n  /// This function takes as parameters the necessary info to do (2).\n  std::string LookupWithFallback(const std::string& var, const EvalString* eval,\n                                 Env* env);\n\nprivate:\n  std::map<std::string, std::string> bindings_;\n  std::map<std::string, std::unique_ptr<const Rule>> rules_;\n  BindingEnv* parent_;\n};\n\n#endif  // NINJA_EVAL_ENV_H_\n"
  },
  {
    "path": "src/exit_status.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_EXIT_STATUS_H_\n#define NINJA_EXIT_STATUS_H_\n\n// The underlying type of the ExitStatus enum, used to represent a platform-specific\n// process exit code.\n#ifdef _WIN32\n#define EXIT_STATUS_TYPE unsigned long\n#else  // !_WIN32\n#define EXIT_STATUS_TYPE int\n#endif  // !_WIN32\n\n\nenum ExitStatus : EXIT_STATUS_TYPE {\n  ExitSuccess=0,\n  ExitFailure,\n  ExitInterrupted=130,\n};\n\n#endif  // NINJA_EXIT_STATUS_H_\n"
  },
  {
    "path": "src/explanations.h",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdarg.h>\n#include <stdio.h>\n\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n/// A class used to record a list of explanation strings associated\n/// with a given 'item' pointer. This is used to implement the\n/// `-d explain` feature.\nstruct Explanations {\n public:\n  /// Record an explanation for |item| if this instance is enabled.\n  void Record(const void* item, const char* fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n    RecordArgs(item, fmt, args);\n    va_end(args);\n  }\n\n  /// Same as Record(), but uses a va_list to pass formatting arguments.\n  void RecordArgs(const void* item, const char* fmt, va_list args) {\n    char buffer[1024];\n    vsnprintf(buffer, sizeof(buffer), fmt, args);\n    map_[item].emplace_back(buffer);\n  }\n\n  /// Lookup the explanations recorded for |item|, and append them\n  /// to |*out|, if any.\n  void LookupAndAppend(const void* item, std::vector<std::string>* out) {\n    auto it = map_.find(item);\n    if (it == map_.end())\n      return;\n\n    for (const auto& explanation : it->second)\n      out->push_back(explanation);\n  }\n\n private:\n  std::unordered_map<const void*, std::vector<std::string>> map_;\n};\n\n/// Convenience wrapper for an Explanations pointer, which can be null\n/// if no explanations need to be recorded.\nstruct OptionalExplanations {\n  OptionalExplanations(Explanations* explanations)\n      : explanations_(explanations) {}\n\n  void Record(const void* item, const char* fmt, ...) {\n    if (explanations_) {\n      va_list args;\n      va_start(args, fmt);\n      explanations_->RecordArgs(item, fmt, args);\n      va_end(args);\n    }\n  }\n\n  void RecordArgs(const void* item, const char* fmt, va_list args) {\n    if (explanations_)\n      explanations_->RecordArgs(item, fmt, args);\n  }\n\n  void LookupAndAppend(const void* item, std::vector<std::string>* out) {\n    if (explanations_)\n      explanations_->LookupAndAppend(item, out);\n  }\n\n  Explanations* ptr() const { return explanations_; }\n\n private:\n  Explanations* explanations_;\n};\n"
  },
  {
    "path": "src/explanations_test.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"explanations.h\"\n\n#include \"test.h\"\n\nnamespace {\n\nconst void* MakeItem(size_t v) {\n  return reinterpret_cast<const void*>(v);\n}\n\n}  // namespace\n\nTEST(Explanations, Explanations) {\n  Explanations exp;\n\n  exp.Record(MakeItem(1), \"first explanation\");\n  exp.Record(MakeItem(1), \"second explanation\");\n  exp.Record(MakeItem(2), \"third explanation\");\n  exp.Record(MakeItem(2), \"fourth %s\", \"explanation\");\n\n  std::vector<std::string> list;\n\n  exp.LookupAndAppend(MakeItem(0), &list);\n  ASSERT_TRUE(list.empty());\n\n  exp.LookupAndAppend(MakeItem(1), &list);\n  ASSERT_EQ(2u, list.size());\n  EXPECT_EQ(list[0], \"first explanation\");\n  EXPECT_EQ(list[1], \"second explanation\");\n\n  exp.LookupAndAppend(MakeItem(2), &list);\n  ASSERT_EQ(4u, list.size());\n  EXPECT_EQ(list[0], \"first explanation\");\n  EXPECT_EQ(list[1], \"second explanation\");\n  EXPECT_EQ(list[2], \"third explanation\");\n  EXPECT_EQ(list[3], \"fourth explanation\");\n}\n\nTEST(Explanations, OptionalExplanationsNonNull) {\n  Explanations parent;\n  OptionalExplanations exp(&parent);\n\n  exp.Record(MakeItem(1), \"first explanation\");\n  exp.Record(MakeItem(1), \"second explanation\");\n  exp.Record(MakeItem(2), \"third explanation\");\n  exp.Record(MakeItem(2), \"fourth %s\", \"explanation\");\n\n  std::vector<std::string> list;\n\n  exp.LookupAndAppend(MakeItem(0), &list);\n  ASSERT_TRUE(list.empty());\n\n  exp.LookupAndAppend(MakeItem(1), &list);\n  ASSERT_EQ(2u, list.size());\n  EXPECT_EQ(list[0], \"first explanation\");\n  EXPECT_EQ(list[1], \"second explanation\");\n\n  exp.LookupAndAppend(MakeItem(2), &list);\n  ASSERT_EQ(4u, list.size());\n  EXPECT_EQ(list[0], \"first explanation\");\n  EXPECT_EQ(list[1], \"second explanation\");\n  EXPECT_EQ(list[2], \"third explanation\");\n  EXPECT_EQ(list[3], \"fourth explanation\");\n}\n\nTEST(Explanations, OptionalExplanationsWithNullPointer) {\n  OptionalExplanations exp(nullptr);\n\n  exp.Record(MakeItem(1), \"first explanation\");\n  exp.Record(MakeItem(1), \"second explanation\");\n  exp.Record(MakeItem(2), \"third explanation\");\n  exp.Record(MakeItem(2), \"fourth %s\", \"explanation\");\n\n  std::vector<std::string> list;\n  exp.LookupAndAppend(MakeItem(0), &list);\n  ASSERT_TRUE(list.empty());\n\n  exp.LookupAndAppend(MakeItem(1), &list);\n  ASSERT_TRUE(list.empty());\n\n  exp.LookupAndAppend(MakeItem(2), &list);\n  ASSERT_TRUE(list.empty());\n}\n"
  },
  {
    "path": "src/gen_doxygen_mainpage.sh",
    "content": "#!/bin/sh\n\n# Copyright 2011 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -o errexit\nset -o nounset\n\nSTATUS=0\n\n# Print each of its arguments on stderr (one per line) prefixed by the\n# basename of this script.\nstderr()\n{\n  local me=$(basename \"$0\")\n  local i\n  for i\n  do\n    echo >&2 \"$me: $i\"\n  done\n}\n\n# Print each of its arguments on stderr (one per line) prefixed by the\n# basename of this script and 'error'.\nerror()\n{\n  local i\n  for i\n  do\n    stderr \"error: $i\"\n  done\n  STATUS=1\n}\n\ngenerate_header()\n{\n  cat <<EOF\n/**\n * \\\\mainpage\nEOF\n}\n\ngenerate_footer()\n{\n  cat <<EOF\n */\nEOF\n}\n\ninclude_file()\n{\n  local file=\"$1\"\n  if ! [ -r \"$file\" ]\n  then\n    error \"'$file' is not readable.\"\n    return\n  fi\n  cat <<EOF\n * \\\\section $file\n * \\\\verbatim\nEOF\n  cat < \"$file\"\n  cat <<EOF\n \\\\endverbatim\nEOF\n}\n\nif [ $# -eq 0 ]\nthen\n  echo >&2 \"usage: $0 inputs...\"\n  exit 1\nfi\n\ngenerate_header\nfor i in \"$@\"\ndo\n  include_file \"$i\"\ndone\ngenerate_footer\n\nexit $STATUS\n"
  },
  {
    "path": "src/getopt.c",
    "content": "/****************************************************************************\n\ngetopt.c - Read command line options\n\nAUTHOR: Gregory Pietsch\nCREATED Fri Jan 10 21:13:05 1997\n\nDESCRIPTION:\n\nThe getopt() function parses the command line arguments.  Its arguments argc\nand argv are the argument count and array as passed to the main() function\non program invocation.  The argument optstring is a list of available option\ncharacters.  If such a character is followed by a colon (`:'), the option\ntakes an argument, which is placed in optarg.  If such a character is\nfollowed by two colons, the option takes an optional argument, which is\nplaced in optarg.  If the option does not take an argument, optarg is NULL.\n\nThe external variable optind is the index of the next array element of argv\nto be processed; it communicates from one call to the next which element to\nprocess.\n\nThe getopt_long() function works like getopt() except that it also accepts\nlong options started by two dashes `--'.  If these take values, it is either\nin the form\n\n--arg=value\n\n or\n\n--arg value\n\nIt takes the additional arguments longopts which is a pointer to the first\nelement of an array of type GETOPT_LONG_OPTION_T.  The last element of the\narray has to be filled with NULL for the name field.\n\nThe longind pointer points to the index of the current long option relative\nto longopts if it is non-NULL.\n\nThe getopt() function returns the option character if the option was found\nsuccessfully, `:' if there was a missing parameter for one of the options,\n`?' for an unknown option character, and EOF for the end of the option list.\n\nThe getopt_long() function's return value is described in the header file.\n\nThe function getopt_long_only() is identical to getopt_long(), except that a\nplus sign `+' can introduce long options as well as `--'.\n\nThe following describes how to deal with options that follow non-option\nargv-elements.\n\nIf the caller did not specify anything, the default is REQUIRE_ORDER if the\nenvironment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.\n\nREQUIRE_ORDER means don't recognize them as options; stop option processing\nwhen the first non-option is seen.  This is what Unix does.  This mode of\noperation is selected by either setting the environment variable\nPOSIXLY_CORRECT, or using `+' as the first character of the optstring\nparameter.\n\nPERMUTE is the default.  We permute the contents of ARGV as we scan, so that\neventually all the non-options are at the end.  This allows options to be\ngiven in any order, even with programs that were not written to expect this.\n\nRETURN_IN_ORDER is an option available to programs that were written to\nexpect options and other argv-elements in any order and that care about the\nordering of the two.  We describe each non-option argv-element as if it were\nthe argument of an option with character code 1.  Using `-' as the first\ncharacter of the optstring parameter selects this mode of operation.\n\nThe special argument `--' forces an end of option-scanning regardless of the\nvalue of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause\ngetopt() and friends to return EOF with optind != argc.\n\nCOPYRIGHT NOTICE AND DISCLAIMER:\n\nCopyright (C) 1997 Gregory Pietsch\n\nThis file and the accompanying getopt.h header file are hereby placed in the\npublic domain without restrictions.  Just give the author credit, don't\nclaim you wrote it or prevent anyone else from using it.\n\nGregory Pietsch's current e-mail address:\ngpietsch@comcast.net\n****************************************************************************/\n\n/* include files */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef GETOPT_H\n#include \"getopt.h\"\n#endif\n\n/* macros */\n\n/* types */\ntypedef enum GETOPT_ORDERING_T\n{\n  PERMUTE,\n  RETURN_IN_ORDER,\n  REQUIRE_ORDER\n} GETOPT_ORDERING_T;\n\n/* globally-defined variables */\nchar *optarg = NULL;\nint optind = 0;\nint opterr = 1;\nint optopt = '?';\n\n/* functions */\n\n/* reverse_argv_elements:  reverses num elements starting at argv */\nstatic void\nreverse_argv_elements (char **argv, int num)\n{\n  int i;\n  char *tmp;\n\n  for (i = 0; i < (num >> 1); i++)\n    {\n      tmp = argv[i];\n      argv[i] = argv[num - i - 1];\n      argv[num - i - 1] = tmp;\n    }\n}\n\n/* permute: swap two blocks of argv-elements given their lengths */\nstatic void\npermute (char **argv, int len1, int len2)\n{\n  reverse_argv_elements (argv, len1);\n  reverse_argv_elements (argv, len1 + len2);\n  reverse_argv_elements (argv, len2);\n}\n\n/* is_option: is this argv-element an option or the end of the option list? */\nstatic int\nis_option (char *argv_element, int only)\n{\n  return ((argv_element == NULL)\n          || (argv_element[0] == '-') || (only && argv_element[0] == '+'));\n}\n\n/* getopt_internal:  the function that does all the dirty work */\nstatic int\ngetopt_internal (int argc, char **argv, char *shortopts,\n                 GETOPT_LONG_OPTION_T * longopts, int *longind, int only)\n{\n  GETOPT_ORDERING_T ordering = PERMUTE;\n  static size_t optwhere = 0;\n  size_t permute_from = 0;\n  int num_nonopts = 0;\n  int optindex = 0;\n  size_t match_chars = 0;\n  char *possible_arg = NULL;\n  int longopt_match = -1;\n  int has_arg = -1;\n  char *cp = NULL;\n  int arg_next = 0;\n\n  /* first, deal with silly parameters and easy stuff */\n  if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))\n    return (optopt = '?');\n  if (optind >= argc || argv[optind] == NULL)\n    return EOF;\n  if (strcmp (argv[optind], \"--\") == 0)\n    {\n      optind++;\n      return EOF;\n    }\n  /* if this is our first time through */\n  if (optind == 0)\n    optind = optwhere = 1;\n\n  /* define ordering */\n  if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))\n    {\n      ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;\n      shortopts++;\n    }\n  else\n    ordering = (getenv (\"POSIXLY_CORRECT\") != NULL) ? REQUIRE_ORDER : PERMUTE;\n\n  /*\n   * based on ordering, find our next option, if we're at the beginning of\n   * one\n   */\n  if (optwhere == 1)\n    {\n      switch (ordering)\n        {\n        case PERMUTE:\n          permute_from = optind;\n          num_nonopts = 0;\n          while (!is_option (argv[optind], only))\n            {\n              optind++;\n              num_nonopts++;\n            }\n          if (argv[optind] == NULL)\n            {\n              /* no more options */\n              optind = permute_from;\n              return EOF;\n            }\n          else if (strcmp (argv[optind], \"--\") == 0)\n            {\n              /* no more options, but have to get `--' out of the way */\n              permute (argv + permute_from, num_nonopts, 1);\n              optind = permute_from + 1;\n              return EOF;\n            }\n          break;\n        case RETURN_IN_ORDER:\n          if (!is_option (argv[optind], only))\n            {\n              optarg = argv[optind++];\n              return (optopt = 1);\n            }\n          break;\n        case REQUIRE_ORDER:\n          if (!is_option (argv[optind], only))\n            return EOF;\n          break;\n        }\n    }\n  /* we've got an option, so parse it */\n\n  /* first, is it a long option? */\n  if (longopts != NULL\n      && (memcmp (argv[optind], \"--\", 2) == 0\n          || (only && argv[optind][0] == '+')) && optwhere == 1)\n    {\n      /* handle long options */\n      if (memcmp (argv[optind], \"--\", 2) == 0)\n        optwhere = 2;\n      longopt_match = -1;\n      possible_arg = strchr (argv[optind] + optwhere, '=');\n      if (possible_arg == NULL)\n        {\n          /* no =, so next argv might be arg */\n          match_chars = strlen (argv[optind]);\n          possible_arg = argv[optind] + match_chars;\n          match_chars = match_chars - optwhere;\n        }\n      else\n        match_chars = (possible_arg - argv[optind]) - optwhere;\n      for (optindex = 0; longopts[optindex].name != NULL; optindex++)\n        {\n          if (memcmp (argv[optind] + optwhere,\n                      longopts[optindex].name, match_chars) == 0)\n            {\n              /* do we have an exact match? */\n              if (match_chars == strlen (longopts[optindex].name))\n                {\n                  longopt_match = optindex;\n                  break;\n                }\n              /* do any characters match? */\n              else\n                {\n                  if (longopt_match < 0)\n                    longopt_match = optindex;\n                  else\n                    {\n                      /* we have ambiguous options */\n                      if (opterr)\n                        fprintf (stderr, \"%s: option `%s' is ambiguous \"\n                                 \"(could be `--%s' or `--%s')\\n\",\n                                 argv[0],\n                                 argv[optind],\n                                 longopts[longopt_match].name,\n                                 longopts[optindex].name);\n                      return (optopt = '?');\n                    }\n                }\n            }\n        }\n      if (longopt_match >= 0)\n        has_arg = longopts[longopt_match].has_arg;\n    }\n  /* if we didn't find a long option, is it a short option? */\n  if (longopt_match < 0 && shortopts != NULL)\n    {\n      cp = strchr (shortopts, argv[optind][optwhere]);\n      if (cp == NULL)\n        {\n          /* couldn't find option in shortopts */\n          if (opterr)\n            fprintf (stderr,\n                     \"%s: invalid option -- `-%c'\\n\",\n                     argv[0], argv[optind][optwhere]);\n          optwhere++;\n          if (argv[optind][optwhere] == '\\0')\n            {\n              optind++;\n              optwhere = 1;\n            }\n          return (optopt = '?');\n        }\n      has_arg = ((cp[1] == ':')\n                 ? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument);\n      possible_arg = argv[optind] + optwhere + 1;\n      optopt = *cp;\n    }\n  /* get argument and reset optwhere */\n  arg_next = 0;\n  switch (has_arg)\n    {\n    case OPTIONAL_ARG:\n      if (*possible_arg == '=')\n        possible_arg++;\n      if (*possible_arg != '\\0')\n        {\n          optarg = possible_arg;\n          optwhere = 1;\n        }\n      else\n        optarg = NULL;\n      break;\n    case required_argument:\n      if (*possible_arg == '=')\n        possible_arg++;\n      if (*possible_arg != '\\0')\n        {\n          optarg = possible_arg;\n          optwhere = 1;\n        }\n      else if (optind + 1 >= argc)\n        {\n          if (opterr)\n            {\n              fprintf (stderr, \"%s: argument required for option `\", argv[0]);\n              if (longopt_match >= 0)\n                fprintf (stderr, \"--%s'\\n\", longopts[longopt_match].name);\n              else\n                fprintf (stderr, \"-%c'\\n\", *cp);\n            }\n          optind++;\n          return (optopt = ':');\n        }\n      else\n        {\n          optarg = argv[optind + 1];\n          arg_next = 1;\n          optwhere = 1;\n        }\n      break;\n    case no_argument:\n      if (longopt_match < 0)\n        {\n          optwhere++;\n          if (argv[optind][optwhere] == '\\0')\n            optwhere = 1;\n        }\n      else\n        optwhere = 1;\n      optarg = NULL;\n      break;\n    }\n\n  /* do we have to permute or otherwise modify optind? */\n  if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)\n    {\n      permute (argv + permute_from, num_nonopts, 1 + arg_next);\n      optind = permute_from + 1 + arg_next;\n    }\n  else if (optwhere == 1)\n    optind = optind + 1 + arg_next;\n\n  /* finally return */\n  if (longopt_match >= 0)\n    {\n      if (longind != NULL)\n        *longind = longopt_match;\n      if (longopts[longopt_match].flag != NULL)\n        {\n          *(longopts[longopt_match].flag) = longopts[longopt_match].val;\n          return 0;\n        }\n      else\n        return longopts[longopt_match].val;\n    }\n  else\n    return optopt;\n}\n\n#ifndef _AIX\nint\ngetopt (int argc, char **argv, char *optstring)\n{\n  return getopt_internal (argc, argv, optstring, NULL, NULL, 0);\n}\n#endif\n\nint\ngetopt_long (int argc, char **argv, const char *shortopts,\n             const GETOPT_LONG_OPTION_T * longopts, int *longind)\n{\n  return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0);\n}\n\nint\ngetopt_long_only (int argc, char **argv, const char *shortopts,\n                  const GETOPT_LONG_OPTION_T * longopts, int *longind)\n{\n  return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1);\n}\n\n/* end of file GETOPT.C */\n"
  },
  {
    "path": "src/getopt.h",
    "content": "#ifndef GETOPT_H\n#define GETOPT_H\n\n/* include files needed by this include file */\n\n/* macros defined by this include file */\n#define no_argument       0\n#define required_argument 1\n#define OPTIONAL_ARG      2\n\n/* types defined by this include file */\n\n/* GETOPT_LONG_OPTION_T: The type of long option */\ntypedef struct GETOPT_LONG_OPTION_T\n{\n  const char *name;             /* the name of the long option */\n  int has_arg;                  /* one of the above macros */\n  int *flag;                    /* determines if getopt_long() returns a\n                                 * value for a long option; if it is\n                                 * non-NULL, 0 is returned as a function\n                                 * value and the value of val is stored in\n                                 * the area pointed to by flag.  Otherwise,\n                                 * val is returned. */\n  int val;                      /* determines the value to return if flag is\n                                 * NULL. */\n} GETOPT_LONG_OPTION_T;\n\ntypedef GETOPT_LONG_OPTION_T option;\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\n  /* externally-defined variables */\n  extern char *optarg;\n  extern int optind;\n  extern int opterr;\n  extern int optopt;\n\n  /* function prototypes */\n#ifndef _AIX\n  int getopt (int argc, char **argv, char *optstring);\n#endif\n  int getopt_long (int argc, char **argv, const char *shortopts,\n                   const GETOPT_LONG_OPTION_T * longopts, int *longind);\n  int getopt_long_only (int argc, char **argv, const char *shortopts,\n                        const GETOPT_LONG_OPTION_T * longopts, int *longind);\n\n#ifdef __cplusplus\n};\n\n#endif\n\n#endif /* GETOPT_H */\n\n/* END OF FILE getopt.h */\n"
  },
  {
    "path": "src/graph.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"graph.h\"\n\n#include <algorithm>\n#include <deque>\n#include <assert.h>\n#include <stdio.h>\n\n#include \"build_log.h\"\n#include \"debug_flags.h\"\n#include \"depfile_parser.h\"\n#include \"deps_log.h\"\n#include \"disk_interface.h\"\n#include \"manifest_parser.h\"\n#include \"metrics.h\"\n#include \"state.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nbool Node::Stat(DiskInterface* disk_interface, string* err) {\n  mtime_ = disk_interface->Stat(path_, err);\n  if (mtime_ == -1) {\n    return false;\n  }\n  exists_ = (mtime_ != 0) ? ExistenceStatusExists : ExistenceStatusMissing;\n  return true;\n}\n\nvoid Node::UpdatePhonyMtime(TimeStamp mtime) {\n  if (!exists()) {\n    mtime_ = std::max(mtime_, mtime);\n  }\n}\n\nbool DependencyScan::RecomputeDirty(Node* initial_node,\n                                    std::vector<Node*>* validation_nodes,\n                                    string* err) {\n  std::vector<Node*> stack;\n  std::vector<Node*> new_validation_nodes;\n\n  std::deque<Node*> nodes(1, initial_node);\n\n  // RecomputeNodeDirty might return new validation nodes that need to be\n  // checked for dirty state, keep a queue of nodes to visit.\n  while (!nodes.empty()) {\n    Node* node = nodes.front();\n    nodes.pop_front();\n\n    stack.clear();\n    new_validation_nodes.clear();\n\n    if (!RecomputeNodeDirty(node, &stack, &new_validation_nodes, err))\n      return false;\n    nodes.insert(nodes.end(), new_validation_nodes.begin(),\n                              new_validation_nodes.end());\n    if (!new_validation_nodes.empty()) {\n      assert(validation_nodes &&\n          \"validations require RecomputeDirty to be called with validation_nodes\");\n      validation_nodes->insert(validation_nodes->end(),\n                           new_validation_nodes.begin(),\n                           new_validation_nodes.end());\n    }\n  }\n\n  return true;\n}\n\nbool DependencyScan::RecomputeNodeDirty(Node* node, std::vector<Node*>* stack,\n                                        std::vector<Node*>* validation_nodes,\n                                        string* err) {\n  Edge* edge = node->in_edge();\n  if (!edge) {\n    // If we already visited this leaf node then we are done.\n    if (node->status_known())\n      return true;\n    // This node has no in-edge; it is dirty if it is missing.\n    if (!node->StatIfNecessary(disk_interface_, err))\n      return false;\n    if (!node->exists())\n      explanations_.Record(node, \"%s has no in-edge and is missing\",\n                           node->path().c_str());\n    node->set_dirty(!node->exists());\n    return true;\n  }\n\n  // If we already finished this edge then we are done.\n  if (edge->mark_ == Edge::VisitDone)\n    return true;\n\n  // If we encountered this edge earlier in the call stack we have a cycle.\n  if (!VerifyDAG(node, stack, err))\n    return false;\n\n  // Mark the edge temporarily while in the call stack.\n  edge->mark_ = Edge::VisitInStack;\n  stack->push_back(node);\n\n  bool dirty = false;\n  edge->outputs_ready_ = true;\n  edge->deps_missing_ = false;\n\n  if (!edge->deps_loaded_) {\n    // This is our first encounter with this edge.\n    // If there is a pending dyndep file, visit it now:\n    // * If the dyndep file is ready then load it now to get any\n    //   additional inputs and outputs for this and other edges.\n    //   Once the dyndep file is loaded it will no longer be pending\n    //   if any other edges encounter it, but they will already have\n    //   been updated.\n    // * If the dyndep file is not ready then since is known to be an\n    //   input to this edge, the edge will not be considered ready below.\n    //   Later during the build the dyndep file will become ready and be\n    //   loaded to update this edge before it can possibly be scheduled.\n    if (edge->dyndep_ && edge->dyndep_->dyndep_pending()) {\n      if (!RecomputeNodeDirty(edge->dyndep_, stack, validation_nodes, err))\n        return false;\n\n      if (!edge->dyndep_->in_edge() ||\n          edge->dyndep_->in_edge()->outputs_ready()) {\n        // The dyndep file is ready, so load it now.\n        if (!LoadDyndeps(edge->dyndep_, err))\n          return false;\n      }\n    }\n  }\n\n  // Load output mtimes so we can compare them to the most recent input below.\n  for (vector<Node*>::iterator o = edge->outputs_.begin();\n       o != edge->outputs_.end(); ++o) {\n    if (err) {\n      *err = \"\";\n    }\n    if (!(*o)->StatIfNecessary(disk_interface_, err))\n      return false;\n  }\n\n  if (!edge->deps_loaded_) {\n    // This is our first encounter with this edge.  Load discovered deps.\n    edge->deps_loaded_ = true;\n    if (!dep_loader_.LoadDeps(edge, err)) {\n      if (!err->empty())\n        return false;\n      // Failed to load dependency info: rebuild to regenerate it.\n      // LoadDeps() did explanations_->Record() already, no need to do it here.\n      dirty = edge->deps_missing_ = true;\n    }\n  }\n\n  // Store any validation nodes from the edge for adding to the initial\n  // nodes.  Don't recurse into them, that would trigger the dependency\n  // cycle detector if the validation node depends on this node.\n  // RecomputeDirty will add the validation nodes to the initial nodes\n  // and recurse into them.\n  validation_nodes->insert(validation_nodes->end(),\n      edge->validations_.begin(), edge->validations_.end());\n\n  // Visit all inputs; we're dirty if any of the inputs are dirty.\n  Node* most_recent_input = NULL;\n  for (vector<Node*>::iterator i = edge->inputs_.begin();\n       i != edge->inputs_.end(); ++i) {\n    // Visit this input.\n    if (!RecomputeNodeDirty(*i, stack, validation_nodes, err))\n      return false;\n\n    // If an input is not ready, neither are our outputs.\n    if (Edge* in_edge = (*i)->in_edge()) {\n      if (!in_edge->outputs_ready_)\n        edge->outputs_ready_ = false;\n    }\n\n    if (!edge->is_order_only(i - edge->inputs_.begin())) {\n      // If a regular input is dirty (or missing), we're dirty.\n      // Otherwise consider mtime.\n      if ((*i)->dirty()) {\n        explanations_.Record(node, \"%s is dirty\", (*i)->path().c_str());\n        dirty = true;\n      } else {\n        if (!most_recent_input || (*i)->mtime() > most_recent_input->mtime()) {\n          most_recent_input = *i;\n        }\n      }\n    }\n  }\n\n  // We may also be dirty due to output state: missing outputs, out of\n  // date outputs, etc.  Visit all outputs and determine whether they're dirty.\n  if (!dirty)\n    if (!RecomputeOutputsDirty(edge, most_recent_input, &dirty, err))\n      return false;\n\n  // Finally, visit each output and update their dirty state if necessary.\n  for (vector<Node*>::iterator o = edge->outputs_.begin();\n       o != edge->outputs_.end(); ++o) {\n    if (dirty)\n      (*o)->MarkDirty();\n  }\n\n  // If an edge is dirty, its outputs are normally not ready.  (It's\n  // possible to be clean but still not be ready in the presence of\n  // order-only inputs.)\n  // But phony edges with no inputs have nothing to do, so are always\n  // ready.\n  if (dirty && !(edge->is_phony() && edge->inputs_.empty()))\n    edge->outputs_ready_ = false;\n\n  // Mark the edge as finished during this walk now that it will no longer\n  // be in the call stack.\n  edge->mark_ = Edge::VisitDone;\n  assert(stack->back() == node);\n  stack->pop_back();\n\n  return true;\n}\n\nbool DependencyScan::VerifyDAG(Node* node, vector<Node*>* stack, string* err) {\n  Edge* edge = node->in_edge();\n  assert(edge != NULL);\n\n  // If we have no temporary mark on the edge then we do not yet have a cycle.\n  if (edge->mark_ != Edge::VisitInStack)\n    return true;\n\n  // We have this edge earlier in the call stack.  Find it.\n  vector<Node*>::iterator start = stack->begin();\n  while (start != stack->end() && (*start)->in_edge() != edge)\n    ++start;\n  assert(start != stack->end());\n\n  // Make the cycle clear by reporting its start as the node at its end\n  // instead of some other output of the starting edge.  For example,\n  // running 'ninja b' on\n  //   build a b: cat c\n  //   build c: cat a\n  // should report a -> c -> a instead of b -> c -> a.\n  *start = node;\n\n  // Construct the error message rejecting the cycle.\n  *err = \"dependency cycle: \";\n  for (vector<Node*>::const_iterator i = start; i != stack->end(); ++i) {\n    err->append((*i)->path());\n    err->append(\" -> \");\n  }\n  err->append((*start)->path());\n\n  if ((start + 1) == stack->end() && edge->maybe_phonycycle_diagnostic()) {\n    // The manifest parser would have filtered out the self-referencing\n    // input if it were not configured to allow the error.\n    err->append(\" [-w phonycycle=err]\");\n  }\n\n  return false;\n}\n\nbool DependencyScan::RecomputeOutputsDirty(Edge* edge, Node* most_recent_input,\n                                           bool* outputs_dirty, string* err) {\n  string command = edge->EvaluateCommand(/*incl_rsp_file=*/true);\n  for (vector<Node*>::iterator o = edge->outputs_.begin();\n       o != edge->outputs_.end(); ++o) {\n    if (RecomputeOutputDirty(edge, most_recent_input, command, *o)) {\n      *outputs_dirty = true;\n      return true;\n    }\n  }\n  return true;\n}\n\nbool DependencyScan::RecomputeOutputDirty(const Edge* edge,\n                                          const Node* most_recent_input,\n                                          const string& command,\n                                          Node* output) {\n  if (edge->is_phony()) {\n    // Phony edges don't write any output.  Outputs are only dirty if\n    // there are no inputs or validations and we're missing the output.\n    // If a phony target has inputs or validations, or the output exists,\n    // they are used for dirty calculation instead of this fallback.\n    if (edge->inputs_.empty() && edge->validations_.empty() &&\n        !output->exists()) {\n      explanations_.Record(\n          output, \"output %s of phony edge with no inputs doesn't exist\",\n          output->path().c_str());\n      return true;\n    }\n\n    // Update the mtime with the newest input. Dependents can thus call mtime()\n    // on the fake node and get the latest mtime of the dependencies\n    if (most_recent_input) {\n      output->UpdatePhonyMtime(most_recent_input->mtime());\n    }\n\n    // Phony edges are clean, nothing to do\n    return false;\n  }\n\n  // Dirty if we're missing the output.\n  if (!output->exists()) {\n    explanations_.Record(output, \"output %s doesn't exist\",\n                         output->path().c_str());\n    return true;\n  }\n\n  BuildLog::LogEntry* entry = 0;\n\n  // If this is a restat rule, we may have cleaned the output in a\n  // previous run and stored the command start time in the build log.\n  // We don't want to consider a restat rule's outputs as dirty unless\n  // an input changed since the last run, so we'll skip checking the\n  // output file's actual mtime and simply check the recorded mtime from\n  // the log against the most recent input's mtime (see below)\n  bool used_restat = false;\n  if (edge->GetBindingBool(\"restat\") && build_log() &&\n      (entry = build_log()->LookupByOutput(output->path()))) {\n    used_restat = true;\n  }\n\n  // Dirty if the output is older than the input.\n  if (!used_restat && most_recent_input && output->mtime() < most_recent_input->mtime()) {\n    explanations_.Record(output,\n                         \"output %s older than most recent input %s \"\n                         \"(%\" PRId64 \" vs %\" PRId64 \")\",\n                         output->path().c_str(),\n                         most_recent_input->path().c_str(), output->mtime(),\n                         most_recent_input->mtime());\n    return true;\n  }\n\n  if (build_log()) {\n    bool generator = edge->GetBindingBool(\"generator\");\n    if (entry || (entry = build_log()->LookupByOutput(output->path()))) {\n      if (!generator &&\n          BuildLog::LogEntry::HashCommand(command) != entry->command_hash) {\n        // May also be dirty due to the command changing since the last build.\n        // But if this is a generator rule, the command changing does not make us\n        // dirty.\n        explanations_.Record(output, \"command line changed for %s\",\n                             output->path().c_str());\n        return true;\n      }\n      if (most_recent_input && entry->mtime < most_recent_input->mtime()) {\n        // May also be dirty due to the mtime in the log being older than the\n        // mtime of the most recent input.  This can occur even when the mtime\n        // on disk is newer if a previous run wrote to the output file but\n        // exited with an error or was interrupted. If this was a restat rule,\n        // then we only check the recorded mtime against the most recent input\n        // mtime and ignore the actual output's mtime above.\n        explanations_.Record(\n            output,\n            \"recorded mtime of %s older than most recent input %s (%\" PRId64\n            \" vs %\" PRId64 \")\",\n            output->path().c_str(), most_recent_input->path().c_str(),\n            entry->mtime, most_recent_input->mtime());\n        return true;\n      }\n    }\n    if (!entry && !generator) {\n      explanations_.Record(output, \"command line not found in log for %s\",\n                           output->path().c_str());\n      return true;\n    }\n  }\n\n  return false;\n}\n\nbool DependencyScan::LoadDyndeps(Node* node, string* err) const {\n  return dyndep_loader_.LoadDyndeps(node, err);\n}\n\nbool DependencyScan::LoadDyndeps(Node* node, DyndepFile* ddf,\n                                 string* err) const {\n  return dyndep_loader_.LoadDyndeps(node, ddf, err);\n}\n\nbool Edge::AllInputsReady() const {\n  for (vector<Node*>::const_iterator i = inputs_.begin();\n       i != inputs_.end(); ++i) {\n    if ((*i)->in_edge() && !(*i)->in_edge()->outputs_ready())\n      return false;\n  }\n  return true;\n}\n\n/// An Env for an Edge, providing $in and $out.\nstruct EdgeEnv : public Env {\n  enum EscapeKind { kShellEscape, kDoNotEscape };\n\n  EdgeEnv(const Edge* const edge, const EscapeKind escape)\n      : edge_(edge), escape_in_out_(escape), recursive_(false) {}\n  virtual string LookupVariable(const string& var);\n\n  /// Given a span of Nodes, construct a list of paths suitable for a command\n  /// line.\n  std::string MakePathList(const Node* const* span, size_t size, char sep) const;\n\n private:\n  std::vector<std::string> lookups_;\n  const Edge* const edge_;\n  EscapeKind escape_in_out_;\n  bool recursive_;\n};\n\nstring EdgeEnv::LookupVariable(const string& var) {\n  if (var == \"in\" || var == \"in_newline\") {\n    int explicit_deps_count =\n        static_cast<int>(edge_->inputs_.size() - edge_->implicit_deps_ -\n                         edge_->order_only_deps_);\n    return MakePathList(edge_->inputs_.data(), explicit_deps_count,\n                        var == \"in\" ? ' ' : '\\n');\n  } else if (var == \"out\") {\n    int explicit_outs_count =\n        static_cast<int>(edge_->outputs_.size() - edge_->implicit_outs_);\n    return MakePathList(&edge_->outputs_[0], explicit_outs_count, ' ');\n  }\n\n  // Technical note about the lookups_ vector.\n  //\n  // This is used to detect cycles during recursive variable expansion\n  // which can be seen as a graph traversal problem. Consider the following\n  // example:\n  //\n  //    rule something\n  //      command = $foo $foo $var1\n  //      var1 = $var2\n  //      var2 = $var3\n  //      var3 = $var1\n  //      foo = FOO\n  //\n  // Each variable definition can be seen as a node in a graph that looks\n  // like the following:\n  //\n  //   command --> foo\n  //      |\n  //      v\n  //    var1 <-----.\n  //      |        |\n  //      v        |\n  //    var2 ---> var3\n  //\n  // The lookups_ vector is used as a stack of visited nodes/variables\n  // during recursive expansion. Entering a node adds an item to the\n  // stack, leaving the node removes it.\n  //\n  // The recursive_ flag is used as a small performance optimization\n  // to never record the starting node in the stack when beginning a new\n  // expansion, since in most cases, expansions are not recursive\n  // at all.\n  //\n  if (recursive_) {\n    auto it = std::find(lookups_.begin(), lookups_.end(), var);\n    if (it != lookups_.end()) {\n      std::string cycle;\n      for (; it != lookups_.end(); ++it)\n        cycle.append(*it + \" -> \");\n      cycle.append(var);\n      Fatal((\"cycle in rule variables: \" + cycle).c_str());\n    }\n  }\n\n  // See notes on BindingEnv::LookupWithFallback.\n  const EvalString* eval = edge_->rule_->GetBinding(var);\n  bool record_varname = recursive_ && eval;\n  if (record_varname)\n    lookups_.push_back(var);\n\n  // In practice, variables defined on rules never use another rule variable.\n  // For performance, only start checking for cycles after the first lookup.\n  recursive_ = true;\n  std::string result = edge_->env_->LookupWithFallback(var, eval, this);\n  if (record_varname)\n    lookups_.pop_back();\n  return result;\n}\n\nstd::string EdgeEnv::MakePathList(const Node* const* const span,\n                                  const size_t size, const char sep) const {\n  string result;\n  for (const Node* const* i = span; i != span + size; ++i) {\n    if (!result.empty())\n      result.push_back(sep);\n    const string& path = (*i)->PathDecanonicalized();\n    if (escape_in_out_ == kShellEscape) {\n#ifdef _WIN32\n      GetWin32EscapedString(path, &result);\n#else\n      GetShellEscapedString(path, &result);\n#endif\n    } else {\n      result.append(path);\n    }\n  }\n  return result;\n}\n\nstd::string Edge::EvaluateCommand(const bool incl_rsp_file) const {\n  string command = GetBinding(\"command\");\n  if (incl_rsp_file) {\n    string rspfile_content = GetBinding(\"rspfile_content\");\n    if (!rspfile_content.empty())\n      command += \";rspfile=\" + rspfile_content;\n  }\n  return command;\n}\n\nstd::string Edge::GetBinding(const std::string& key) const {\n  EdgeEnv env(this, EdgeEnv::kShellEscape);\n  return env.LookupVariable(key);\n}\n\nbool Edge::GetBindingBool(const string& key) const {\n  return !GetBinding(key).empty();\n}\n\nstring Edge::GetUnescapedDepfile() const {\n  EdgeEnv env(this, EdgeEnv::kDoNotEscape);\n  return env.LookupVariable(\"depfile\");\n}\n\nstring Edge::GetUnescapedDyndep() const {\n  EdgeEnv env(this, EdgeEnv::kDoNotEscape);\n  return env.LookupVariable(\"dyndep\");\n}\n\nstd::string Edge::GetUnescapedRspfile() const {\n  EdgeEnv env(this, EdgeEnv::kDoNotEscape);\n  return env.LookupVariable(\"rspfile\");\n}\n\nvoid Edge::Dump(const char* prefix) const {\n  printf(\"%s[ \", prefix);\n  for (vector<Node*>::const_iterator i = inputs_.begin();\n       i != inputs_.end() && *i != NULL; ++i) {\n    printf(\"%s \", (*i)->path().c_str());\n  }\n  printf(\"--%s-> \", rule_->name().c_str());\n  for (vector<Node*>::const_iterator i = outputs_.begin();\n       i != outputs_.end() && *i != NULL; ++i) {\n    printf(\"%s \", (*i)->path().c_str());\n  }\n  if (!validations_.empty()) {\n    printf(\" validations \");\n    for (std::vector<Node*>::const_iterator i = validations_.begin();\n         i != validations_.end() && *i != NULL; ++i) {\n      printf(\"%s \", (*i)->path().c_str());\n    }\n  }\n  if (pool_) {\n    if (!pool_->name().empty()) {\n      printf(\"(in pool '%s')\", pool_->name().c_str());\n    }\n  } else {\n    printf(\"(null pool?)\");\n  }\n  printf(\"] 0x%p\\n\", this);\n}\n\nbool Edge::is_phony() const {\n  return rule_->IsPhony();\n}\n\nbool Edge::use_console() const {\n  return pool() == &State::kConsolePool;\n}\n\nbool Edge::maybe_phonycycle_diagnostic() const {\n  // CMake 2.8.12.x and 3.0.x produced self-referencing phony rules\n  // of the form \"build a: phony ... a ...\".   Restrict our\n  // \"phonycycle\" diagnostic option to the form it used.\n  return is_phony() && outputs_.size() == 1 && implicit_outs_ == 0 &&\n      implicit_deps_ == 0;\n}\n\n// static\nstring Node::PathDecanonicalized(const string& path, uint64_t slash_bits) {\n  string result = path;\n#ifdef _WIN32\n  uint64_t mask = 1;\n  for (char* c = &result[0]; (c = strchr(c, '/')) != NULL;) {\n    if (slash_bits & mask)\n      *c = '\\\\';\n    c++;\n    mask <<= 1;\n  }\n#endif\n  return result;\n}\n\nvoid Node::Dump(const char* prefix) const {\n  printf(\"%s <%s 0x%p> mtime: %\" PRId64 \"%s, (:%s), \",\n         prefix, path().c_str(), this,\n         mtime(), exists() ? \"\" : \" (:missing)\",\n         dirty() ? \" dirty\" : \" clean\");\n  if (in_edge()) {\n    in_edge()->Dump(\"in-edge: \");\n  } else {\n    printf(\"no in-edge\\n\");\n  }\n  printf(\" out edges:\\n\");\n  for (vector<Edge*>::const_iterator e = out_edges().begin();\n       e != out_edges().end() && *e != NULL; ++e) {\n    (*e)->Dump(\" +- \");\n  }\n  if (!validation_out_edges().empty()) {\n    printf(\" validation out edges:\\n\");\n    for (std::vector<Edge*>::const_iterator e = validation_out_edges().begin();\n         e != validation_out_edges().end() && *e != NULL; ++e) {\n      (*e)->Dump(\" +- \");\n    }\n  }\n}\n\nbool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) {\n  string deps_type = edge->GetBinding(\"deps\");\n  if (!deps_type.empty())\n    return LoadDepsFromLog(edge, err);\n\n  string depfile = edge->GetUnescapedDepfile();\n  if (!depfile.empty())\n    return LoadDepFile(edge, depfile, err);\n\n  // No deps to load.\n  return true;\n}\n\nstruct matches {\n  explicit matches(std::vector<StringPiece>::iterator i) : i_(i) {}\n\n  bool operator()(const Node* node) const {\n    StringPiece opath = StringPiece(node->path());\n    return *i_ == opath;\n  }\n\n  std::vector<StringPiece>::iterator i_;\n};\n\nbool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path,\n                                    string* err) {\n  METRIC_RECORD(\"depfile load\");\n  // Read depfile content.  Treat a missing depfile as empty.\n  string content;\n  switch (disk_interface_->ReadFile(path, &content, err)) {\n  case DiskInterface::Okay:\n    break;\n  case DiskInterface::NotFound:\n    err->clear();\n    break;\n  case DiskInterface::OtherError:\n    *err = \"loading '\" + path + \"': \" + *err;\n    return false;\n  }\n  // On a missing depfile: return false and empty *err.\n  Node* first_output = edge->outputs_[0];\n  if (content.empty()) {\n    explanations_.Record(first_output, \"depfile '%s' is missing\", path.c_str());\n    return false;\n  }\n\n  DepfileParser depfile(depfile_parser_options_\n                        ? *depfile_parser_options_\n                        : DepfileParserOptions());\n  string depfile_err;\n  if (!depfile.Parse(&content, &depfile_err)) {\n    *err = path + \": \" + depfile_err;\n    return false;\n  }\n\n  if (depfile.outs_.empty()) {\n    *err = path + \": no outputs declared\";\n    return false;\n  }\n\n  uint64_t unused;\n  std::vector<StringPiece>::iterator primary_out = depfile.outs_.begin();\n  CanonicalizePath(const_cast<char*>(primary_out->str_), &primary_out->len_,\n                   &unused);\n\n  // Check that this depfile matches the edge's output, if not return false to\n  // mark the edge as dirty.\n  StringPiece opath = StringPiece(first_output->path());\n  if (opath != *primary_out) {\n    explanations_.Record(first_output,\n                         \"expected depfile '%s' to mention '%s', got '%s'\",\n                         path.c_str(), first_output->path().c_str(),\n                         primary_out->AsString().c_str());\n    return false;\n  }\n\n  // Ensure that all mentioned outputs are outputs of the edge.\n  for (std::vector<StringPiece>::iterator o = depfile.outs_.begin();\n       o != depfile.outs_.end(); ++o) {\n    matches m(o);\n    if (std::find_if(edge->outputs_.begin(), edge->outputs_.end(), m) == edge->outputs_.end()) {\n      *err = path + \": depfile mentions '\" + o->AsString() + \"' as an output, but no such output was declared\";\n      return false;\n    }\n  }\n\n  return ProcessDepfileDeps(edge, &depfile.ins_, err);\n}\n\nbool ImplicitDepLoader::ProcessDepfileDeps(\n    Edge* edge, std::vector<StringPiece>* depfile_ins, std::string* err) {\n  // Preallocate space in edge->inputs_ to be filled in below.\n  vector<Node*>::iterator implicit_dep =\n      PreallocateSpace(edge, static_cast<int>(depfile_ins->size()));\n\n  // Add all its in-edges.\n  for (std::vector<StringPiece>::iterator i = depfile_ins->begin();\n       i != depfile_ins->end(); ++i, ++implicit_dep) {\n    uint64_t slash_bits;\n    CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits);\n    Node* node = state_->GetNode(*i, slash_bits);\n    *implicit_dep = node;\n    node->AddOutEdge(edge);\n  }\n\n  return true;\n}\n\nbool ImplicitDepLoader::LoadDepsFromLog(Edge* edge, string* err) {\n  // NOTE: deps are only supported for single-target edges.\n  Node* output = edge->outputs_[0];\n  DepsLog::Deps* deps = deps_log_ ? deps_log_->GetDeps(output) : NULL;\n  if (!deps) {\n    explanations_.Record(output, \"deps for '%s' are missing\",\n                         output->path().c_str());\n    return false;\n  }\n\n  // Deps are invalid if the output is newer than the deps.\n  if (output->mtime() > deps->mtime) {\n    explanations_.Record(output,\n                         \"stored deps info out of date for '%s' (%\" PRId64\n                         \" vs %\" PRId64 \")\",\n                         output->path().c_str(), deps->mtime, output->mtime());\n    return false;\n  }\n\n  Node** nodes = deps->nodes;\n  size_t node_count = deps->node_count;\n  edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_,\n                       nodes, nodes + node_count);\n  edge->implicit_deps_ += node_count;\n  for (size_t i = 0; i < node_count; ++i) {\n    nodes[i]->AddOutEdge(edge);\n  }\n  return true;\n}\n\nvector<Node*>::iterator ImplicitDepLoader::PreallocateSpace(Edge* edge,\n                                                            int count) {\n  edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_,\n                       (size_t)count, 0);\n  edge->implicit_deps_ += count;\n  return edge->inputs_.end() - edge->order_only_deps_ - count;\n}\n\nvoid InputsCollector::VisitNode(const Node* node) {\n  const Edge* edge = node->in_edge();\n\n  if (!edge)  // A source file.\n    return;\n\n  // Add inputs of the producing edge to the result,\n  // except if they are themselves produced by a phony\n  // edge.\n  for (const Node* input : edge->inputs_) {\n    if (!visited_nodes_.insert(input).second)\n      continue;\n\n    VisitNode(input);\n\n    const Edge* input_edge = input->in_edge();\n    if (!(input_edge && input_edge->is_phony())) {\n      inputs_.push_back(input);\n    }\n  }\n}\n\nstd::vector<std::string> InputsCollector::GetInputsAsStrings(\n    bool shell_escape) const {\n  std::vector<std::string> result;\n  result.reserve(inputs_.size());\n\n  for (const Node* input : inputs_) {\n    std::string unescaped = input->PathDecanonicalized();\n    if (shell_escape) {\n      std::string path;\n#ifdef _WIN32\n      GetWin32EscapedString(unescaped, &path);\n#else\n      GetShellEscapedString(unescaped, &path);\n#endif\n      result.push_back(std::move(path));\n    } else {\n      result.push_back(std::move(unescaped));\n    }\n  }\n  return result;\n}\n"
  },
  {
    "path": "src/graph.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_GRAPH_H_\n#define NINJA_GRAPH_H_\n\n#include <algorithm>\n#include <queue>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"dyndep.h\"\n#include \"eval_env.h\"\n#include \"explanations.h\"\n#include \"jobserver.h\"\n#include \"timestamp.h\"\n#include \"util.h\"\n\nstruct BuildLog;\nstruct DepfileParserOptions;\nstruct DiskInterface;\nstruct DepsLog;\nstruct Edge;\nstruct Node;\nstruct Pool;\nstruct State;\n\n/// Information about a node in the dependency graph: the file, whether\n/// it's dirty, mtime, etc.\nstruct Node {\n  Node(const std::string& path, uint64_t slash_bits)\n      : path_(path), slash_bits_(slash_bits) {}\n\n  /// Return false on error.\n  bool Stat(DiskInterface* disk_interface, std::string* err);\n\n  /// If the file doesn't exist, set the mtime_ from its dependencies\n  void UpdatePhonyMtime(TimeStamp mtime);\n\n  /// Return false on error.\n  bool StatIfNecessary(DiskInterface* disk_interface, std::string* err) {\n    if (status_known())\n      return true;\n    return Stat(disk_interface, err);\n  }\n\n  /// Mark as not-yet-stat()ed and not dirty.\n  void ResetState() {\n    mtime_ = -1;\n    exists_ = ExistenceStatusUnknown;\n    dirty_ = false;\n  }\n\n  /// Mark the Node as already-stat()ed and missing.\n  void MarkMissing() {\n    if (mtime_ == -1) {\n      mtime_ = 0;\n    }\n    exists_ = ExistenceStatusMissing;\n  }\n\n  bool exists() const {\n    return exists_ == ExistenceStatusExists;\n  }\n\n  bool status_known() const {\n    return exists_ != ExistenceStatusUnknown;\n  }\n\n  const std::string& path() const { return path_; }\n  /// Get |path()| but use slash_bits to convert back to original slash styles.\n  std::string PathDecanonicalized() const {\n    return PathDecanonicalized(path_, slash_bits_);\n  }\n  static std::string PathDecanonicalized(const std::string& path,\n                                         uint64_t slash_bits);\n  uint64_t slash_bits() const { return slash_bits_; }\n\n  TimeStamp mtime() const { return mtime_; }\n\n  bool dirty() const { return dirty_; }\n  void set_dirty(bool dirty) { dirty_ = dirty; }\n  void MarkDirty() { dirty_ = true; }\n\n  bool dyndep_pending() const { return dyndep_pending_; }\n  void set_dyndep_pending(bool pending) { dyndep_pending_ = pending; }\n\n  Edge* in_edge() const { return in_edge_; }\n  void set_in_edge(Edge* edge) { in_edge_ = edge; }\n\n  /// Indicates whether this node was generated from a depfile or dyndep file,\n  /// instead of being a regular input or output from the Ninja manifest.\n  bool generated_by_dep_loader() const { return generated_by_dep_loader_; }\n\n  void set_generated_by_dep_loader(bool value) {\n    generated_by_dep_loader_ = value;\n  }\n\n  int id() const { return id_; }\n  void set_id(int id) { id_ = id; }\n\n  const std::vector<Edge*>& out_edges() const { return out_edges_; }\n  const std::vector<Edge*>& validation_out_edges() const { return validation_out_edges_; }\n  void AddOutEdge(Edge* edge) { out_edges_.push_back(edge); }\n  void AddValidationOutEdge(Edge* edge) { validation_out_edges_.push_back(edge); }\n\n  void Dump(const char* prefix=\"\") const;\n\nprivate:\n  std::string path_;\n\n  /// Set bits starting from lowest for backslashes that were normalized to\n  /// forward slashes by CanonicalizePath. See |PathDecanonicalized|.\n  uint64_t slash_bits_ = 0;\n\n  /// Possible values of mtime_:\n  ///   -1: file hasn't been examined\n  ///   0:  we looked, and file doesn't exist\n  ///   >0: actual file's mtime, or the latest mtime of its dependencies if it doesn't exist\n  TimeStamp mtime_ = -1;\n\n  enum ExistenceStatus : char {\n    /// The file hasn't been examined.\n    ExistenceStatusUnknown,\n    /// The file doesn't exist. mtime_ will be the latest mtime of its dependencies.\n    ExistenceStatusMissing,\n    /// The path is an actual file. mtime_ will be the file's mtime.\n    ExistenceStatusExists\n  };\n  ExistenceStatus exists_ = ExistenceStatusUnknown;\n\n  /// Dirty is true when the underlying file is out-of-date.\n  /// But note that Edge::outputs_ready_ is also used in judging which\n  /// edges to build.\n  bool dirty_ = false;\n\n  /// Store whether dyndep information is expected from this node but\n  /// has not yet been loaded.\n  bool dyndep_pending_ = false;\n\n  /// Set to true when this node comes from a depfile, a dyndep file or the\n  /// deps log. If it does not have a producing edge, the build should not\n  /// abort if it is missing (as for regular source inputs). By default\n  /// all nodes have this flag set to true, since the deps and build logs\n  /// can be loaded before the manifest.\n  bool generated_by_dep_loader_ = true;\n\n  /// A dense integer id for the node, assigned and used by DepsLog.\n  int id_ = -1;\n\n  /// The Edge that produces this Node, or NULL when there is no\n  /// known edge to produce it.\n  Edge* in_edge_ = nullptr;\n\n  /// All Edges that use this Node as an input.\n  std::vector<Edge*> out_edges_;\n\n  /// All Edges that use this Node as a validation.\n  std::vector<Edge*> validation_out_edges_;\n};\n\n/// An edge in the dependency graph; links between Nodes using Rules.\nstruct Edge {\n  enum VisitMark : char {\n    VisitNone,\n    VisitInStack,\n    VisitDone\n  };\n\n  Edge() = default;\n\n  /// Return true if all inputs' in-edges are ready.\n  bool AllInputsReady() const;\n\n  /// Expand all variables in a command and return it as a string.\n  /// If incl_rsp_file is enabled, the string will also contain the\n  /// full contents of a response file (if applicable)\n  std::string EvaluateCommand(bool incl_rsp_file = false) const;\n\n  /// Returns the shell-escaped value of |key|.\n  std::string GetBinding(const std::string& key) const;\n  bool GetBindingBool(const std::string& key) const;\n\n  /// Like GetBinding(\"depfile\"), but without shell escaping.\n  std::string GetUnescapedDepfile() const;\n  /// Like GetBinding(\"dyndep\"), but without shell escaping.\n  std::string GetUnescapedDyndep() const;\n  /// Like GetBinding(\"rspfile\"), but without shell escaping.\n  std::string GetUnescapedRspfile() const;\n\n  void Dump(const char* prefix=\"\") const;\n\n  // critical_path_weight is the priority during build scheduling. The\n  // \"critical path\" between this edge's inputs and any target node is\n  // the path which maximises the sum oof weights along that path.\n  // NOTE: Defaults to -1 as a marker smaller than any valid weight\n  int64_t critical_path_weight() const { return critical_path_weight_; }\n  void set_critical_path_weight(int64_t critical_path_weight) {\n    critical_path_weight_ = critical_path_weight;\n  }\n\n  const Rule* rule_ = nullptr;\n  Pool* pool_ = nullptr;\n  std::vector<Node*> inputs_;\n  std::vector<Node*> outputs_;\n  std::vector<Node*> validations_;\n  Node* dyndep_ = nullptr;\n  BindingEnv* env_ = nullptr;\n  size_t id_ = 0;\n  int64_t critical_path_weight_ = -1;\n\n  /// A Jobserver slot instance. Invalid by default.\n  Jobserver::Slot job_slot_;\n\n  VisitMark mark_ = VisitNone;\n  bool outputs_ready_ = false;\n  bool deps_loaded_ = false;\n  bool deps_missing_ = false;\n  bool generated_by_dep_loader_ = false;\n  TimeStamp command_start_time_ = 0;\n\n  const Rule& rule() const { return *rule_; }\n  Pool* pool() const { return pool_; }\n  int weight() const { return 1; }\n  bool outputs_ready() const { return outputs_ready_; }\n\n  // There are three types of inputs.\n  // 1) explicit deps, which show up as $in on the command line;\n  // 2) implicit deps, which the target depends on implicitly (e.g. C headers),\n  //                   and changes in them cause the target to rebuild;\n  // 3) order-only deps, which are needed before the target builds but which\n  //                     don't cause the target to rebuild.\n  // These are stored in inputs_ in that order, and we keep counts of\n  // #2 and #3 when we need to access the various subsets.\n  int implicit_deps_ = 0;\n  int order_only_deps_ = 0;\n  bool is_implicit(size_t index) {\n    return index >= inputs_.size() - order_only_deps_ - implicit_deps_ &&\n        !is_order_only(index);\n  }\n  bool is_order_only(size_t index) {\n    return index >= inputs_.size() - order_only_deps_;\n  }\n\n  // There are two types of outputs.\n  // 1) explicit outs, which show up as $out on the command line;\n  // 2) implicit outs, which the target generates but are not part of $out.\n  // These are stored in outputs_ in that order, and we keep a count of\n  // #2 to use when we need to access the various subsets.\n  int implicit_outs_ = 0;\n  bool is_implicit_out(size_t index) const {\n    return index >= outputs_.size() - implicit_outs_;\n  }\n\n  bool is_phony() const;\n  bool use_console() const;\n  bool maybe_phonycycle_diagnostic() const;\n\n  // Historical info: how long did this edge take last time,\n  // as per .ninja_log, if known? Defaults to -1 if unknown.\n  int64_t prev_elapsed_time_millis = -1;\n};\n\nstruct EdgeCmp {\n  bool operator()(const Edge* a, const Edge* b) const {\n    return a->id_ < b->id_;\n  }\n};\n\ntypedef std::set<Edge*, EdgeCmp> EdgeSet;\n\n/// ImplicitDepLoader loads implicit dependencies, as referenced via the\n/// \"depfile\" attribute in build files.\nstruct ImplicitDepLoader {\n  ImplicitDepLoader(State* state, DepsLog* deps_log,\n                    DiskInterface* disk_interface,\n                    DepfileParserOptions const* depfile_parser_options,\n                    Explanations* explanations)\n      : state_(state), disk_interface_(disk_interface), deps_log_(deps_log),\n        depfile_parser_options_(depfile_parser_options),\n        explanations_(explanations) {}\n\n  /// Load implicit dependencies for \\a edge.\n  /// @return false on error (without filling \\a err if info is just missing\n  //                          or out of date).\n  bool LoadDeps(Edge* edge, std::string* err);\n\n  DepsLog* deps_log() const {\n    return deps_log_;\n  }\n\n protected:\n  /// Process loaded implicit dependencies for \\a edge and update the graph\n  /// @return false on error (without filling \\a err if info is just missing)\n  virtual bool ProcessDepfileDeps(Edge* edge,\n                                  std::vector<StringPiece>* depfile_ins,\n                                  std::string* err);\n\n  /// Load implicit dependencies for \\a edge from a depfile attribute.\n  /// @return false on error (without filling \\a err if info is just missing).\n  bool LoadDepFile(Edge* edge, const std::string& path, std::string* err);\n\n  /// Load implicit dependencies for \\a edge from the DepsLog.\n  /// @return false on error (without filling \\a err if info is just missing).\n  bool LoadDepsFromLog(Edge* edge, std::string* err);\n\n  /// Preallocate \\a count spaces in the input array on \\a edge, returning\n  /// an iterator pointing at the first new space.\n  std::vector<Node*>::iterator PreallocateSpace(Edge* edge, int count);\n\n  State* state_;\n  DiskInterface* disk_interface_;\n  DepsLog* deps_log_;\n  DepfileParserOptions const* depfile_parser_options_;\n  OptionalExplanations explanations_;\n};\n\n\n/// DependencyScan manages the process of scanning the files in a graph\n/// and updating the dirty/outputs_ready state of all the nodes and edges.\nstruct DependencyScan {\n  DependencyScan(State* state, BuildLog* build_log, DepsLog* deps_log,\n                 DiskInterface* disk_interface,\n                 DepfileParserOptions const* depfile_parser_options,\n                 Explanations* explanations)\n      : build_log_(build_log), disk_interface_(disk_interface),\n        dep_loader_(state, deps_log, disk_interface, depfile_parser_options,\n                    explanations),\n        dyndep_loader_(state, disk_interface), explanations_(explanations) {}\n\n  /// Update the |dirty_| state of the given nodes by transitively inspecting\n  /// their input edges.\n  /// Examine inputs, outputs, and command lines to judge whether an edge\n  /// needs to be re-run, and update outputs_ready_ and each outputs' |dirty_|\n  /// state accordingly.\n  /// Appends any validation nodes found to the nodes parameter.\n  /// Returns false on failure.\n  bool RecomputeDirty(Node* node, std::vector<Node*>* validation_nodes, std::string* err);\n\n  /// Recompute whether any output of the edge is dirty, if so sets |*dirty|.\n  /// Returns false on failure.\n  bool RecomputeOutputsDirty(Edge* edge, Node* most_recent_input,\n                             bool* dirty, std::string* err);\n\n  BuildLog* build_log() const {\n    return build_log_;\n  }\n  void set_build_log(BuildLog* log) {\n    build_log_ = log;\n  }\n\n  DepsLog* deps_log() const {\n    return dep_loader_.deps_log();\n  }\n\n  /// Load a dyndep file from the given node's path and update the\n  /// build graph with the new information.  One overload accepts\n  /// a caller-owned 'DyndepFile' object in which to store the\n  /// information loaded from the dyndep file.\n  bool LoadDyndeps(Node* node, std::string* err) const;\n  bool LoadDyndeps(Node* node, DyndepFile* ddf, std::string* err) const;\n\n private:\n  bool RecomputeNodeDirty(Node* node, std::vector<Node*>* stack,\n                          std::vector<Node*>* validation_nodes, std::string* err);\n  bool VerifyDAG(Node* node, std::vector<Node*>* stack, std::string* err);\n\n  /// Recompute whether a given single output should be marked dirty.\n  /// Returns true if so.\n  bool RecomputeOutputDirty(const Edge* edge, const Node* most_recent_input,\n                            const std::string& command, Node* output);\n\n  void RecordExplanation(const Node* node, const char* fmt, ...);\n\n  BuildLog* build_log_;\n  DiskInterface* disk_interface_;\n  ImplicitDepLoader dep_loader_;\n  DyndepLoader dyndep_loader_;\n  OptionalExplanations explanations_;\n};\n\n// Implements a less comparison for edges by priority, where highest\n// priority is defined lexicographically first by largest critical\n// time, then lowest ID.\n//\n// Including ID means that wherever the critical path weights are the\n// same, the edges are executed in ascending ID order which was\n// historically how all tasks were scheduled.\nstruct EdgePriorityLess {\n  bool operator()(const Edge* e1, const Edge* e2) const {\n    const int64_t cw1 = e1->critical_path_weight();\n    const int64_t cw2 = e2->critical_path_weight();\n    if (cw1 != cw2) {\n      return cw1 < cw2;\n    }\n    return e1->id_ > e2->id_;\n  }\n};\n\n// Reverse of EdgePriorityLess, e.g. to sort by highest priority first\nstruct EdgePriorityGreater {\n  bool operator()(const Edge* e1, const Edge* e2) const {\n    return EdgePriorityLess()(e2, e1);\n  }\n};\n\n// A priority queue holding non-owning Edge pointers. top() will\n// return the edge with the largest critical path weight, and lowest\n// ID if more than one edge has the same critical path weight.\nclass EdgePriorityQueue:\n  public std::priority_queue<Edge*, std::vector<Edge*>, EdgePriorityLess>{\npublic:\n  void clear() {\n    c.clear();\n  }\n};\n\n/// A class used to collect the transitive set of inputs from a given set\n/// of starting nodes. Used to implement the `inputs` tool.\n///\n/// When collecting inputs, the outputs of phony edges are always ignored\n/// from the result, but are followed by the dependency walk.\n///\n/// Usage is:\n/// - Create instance.\n/// - Call VisitNode() for each root node to collect inputs from.\n/// - Call inputs() to retrieve the list of input node pointers.\n/// - Call GetInputsAsStrings() to retrieve the list of inputs as a string\n/// vector.\n///\nstruct InputsCollector {\n  /// Visit a single @arg node during this collection.\n  void VisitNode(const Node* node);\n\n  /// Retrieve list of visited input nodes. A dependency always appears\n  /// before its dependents in the result, but final order depends on the\n  /// order of the VisitNode() calls performed before this.\n  const std::vector<const Node*>& inputs() const { return inputs_; }\n\n  /// Same as inputs(), but returns the list of visited nodes as a list of\n  /// strings, with optional shell escaping.\n  std::vector<std::string> GetInputsAsStrings(bool shell_escape = false) const;\n\n  /// Reset collector state.\n  void Reset() {\n    inputs_.clear();\n    visited_nodes_.clear();\n  }\n\n private:\n  std::vector<const Node*> inputs_;\n  std::set<const Node*> visited_nodes_;\n};\n\n#endif  // NINJA_GRAPH_H_\n"
  },
  {
    "path": "src/graph_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"graph.h\"\n\n#include \"build.h\"\n#include \"command_collector.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nstruct GraphTest : public StateTestWithBuiltinRules {\n  GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL, NULL) {}\n\n  VirtualFileSystem fs_;\n  DependencyScan scan_;\n};\n\nTEST_F(GraphTest, MissingImplicit) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat in | implicit\\n\"));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  // A missing implicit dep *should* make the output dirty.\n  // (In fact, a build will fail.)\n  // This is a change from prior semantics of ninja.\n  EXPECT_TRUE(GetNode(\"out\")->dirty());\n}\n\nTEST_F(GraphTest, ModifiedImplicit) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat in | implicit\\n\"));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out\", \"\");\n  fs_.Tick();\n  fs_.Create(\"implicit\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  // A modified implicit dep should make the output dirty.\n  EXPECT_TRUE(GetNode(\"out\")->dirty());\n}\n\nTEST_F(GraphTest, FunkyMakefilePath) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule catdep\\n\"\n\"  depfile = $out.d\\n\"\n\"  command = cat $in > $out\\n\"\n\"build out.o: catdep foo.cc\\n\"));\n  fs_.Create(\"foo.cc\",  \"\");\n  fs_.Create(\"out.o.d\", \"out.o: ./foo/../implicit.h\\n\");\n  fs_.Create(\"out.o\", \"\");\n  fs_.Tick();\n  fs_.Create(\"implicit.h\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.o\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  // implicit.h has changed, though our depfile refers to it with a\n  // non-canonical path; we should still find it.\n  EXPECT_TRUE(GetNode(\"out.o\")->dirty());\n}\n\nTEST_F(GraphTest, ExplicitImplicit) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule catdep\\n\"\n\"  depfile = $out.d\\n\"\n\"  command = cat $in > $out\\n\"\n\"build implicit.h: cat data\\n\"\n\"build out.o: catdep foo.cc || implicit.h\\n\"));\n  fs_.Create(\"implicit.h\", \"\");\n  fs_.Create(\"foo.cc\", \"\");\n  fs_.Create(\"out.o.d\", \"out.o: implicit.h\\n\");\n  fs_.Create(\"out.o\", \"\");\n  fs_.Tick();\n  fs_.Create(\"data\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.o\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  // We have both an implicit and an explicit dep on implicit.h.\n  // The implicit dep should \"win\" (in the sense that it should cause\n  // the output to be dirty).\n  EXPECT_TRUE(GetNode(\"out.o\")->dirty());\n}\n\nTEST_F(GraphTest, ImplicitOutputParse) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out | out.imp: cat in\\n\"));\n\n  Edge* edge = GetNode(\"out\")->in_edge();\n  EXPECT_EQ(size_t(2), edge->outputs_.size());\n  EXPECT_EQ(\"out\", edge->outputs_[0]->path());\n  EXPECT_EQ(\"out.imp\", edge->outputs_[1]->path());\n  EXPECT_EQ(1, edge->implicit_outs_);\n  EXPECT_EQ(edge, GetNode(\"out.imp\")->in_edge());\n}\n\nTEST_F(GraphTest, ImplicitOutputMissing) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out | out.imp: cat in\\n\"));\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_TRUE(GetNode(\"out\")->dirty());\n  EXPECT_TRUE(GetNode(\"out.imp\")->dirty());\n}\n\nTEST_F(GraphTest, ImplicitOutputOutOfDate) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out | out.imp: cat in\\n\"));\n  fs_.Create(\"out.imp\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_TRUE(GetNode(\"out\")->dirty());\n  EXPECT_TRUE(GetNode(\"out.imp\")->dirty());\n}\n\nTEST_F(GraphTest, ImplicitOutputOnlyParse) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build | out.imp: cat in\\n\"));\n\n  Edge* edge = GetNode(\"out.imp\")->in_edge();\n  EXPECT_EQ(size_t(1), edge->outputs_.size());\n  EXPECT_EQ(\"out.imp\", edge->outputs_[0]->path());\n  EXPECT_EQ(1, edge->implicit_outs_);\n  EXPECT_EQ(edge, GetNode(\"out.imp\")->in_edge());\n}\n\nTEST_F(GraphTest, ImplicitOutputOnlyMissing) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build | out.imp: cat in\\n\"));\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.imp\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_TRUE(GetNode(\"out.imp\")->dirty());\n}\n\nTEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build | out.imp: cat in\\n\"));\n  fs_.Create(\"out.imp\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.imp\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_TRUE(GetNode(\"out.imp\")->dirty());\n}\n\nTEST_F(GraphTest, PathWithCurrentDirectory) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule catdep\\n\"\n\"  depfile = $out.d\\n\"\n\"  command = cat $in > $out\\n\"\n\"build ./out.o: catdep ./foo.cc\\n\"));\n  fs_.Create(\"foo.cc\", \"\");\n  fs_.Create(\"out.o.d\", \"out.o: foo.cc\\n\");\n  fs_.Create(\"out.o\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.o\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_FALSE(GetNode(\"out.o\")->dirty());\n}\n\nTEST_F(GraphTest, RootNodes) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out1: cat in1\\n\"\n\"build mid1: cat in1\\n\"\n\"build out2: cat mid1\\n\"\n\"build out3 out4: cat mid1\\n\"));\n\n  string err;\n  vector<Node*> root_nodes = state_.RootNodes(&err);\n  EXPECT_EQ(4u, root_nodes.size());\n  for (size_t i = 0; i < root_nodes.size(); ++i) {\n    string name = root_nodes[i]->path();\n    EXPECT_EQ(\"out\", name.substr(0, 3));\n  }\n}\n\nTEST_F(GraphTest, InputsCollector) {\n  // Build plan for the following graph:\n  //\n  //      in1\n  //       |___________\n  //       |           |\n  //      ===         ===\n  //       |           |\n  //      out1        mid1\n  //       |       ____|_____\n  //       |      |          |\n  //       |     ===      =======\n  //       |      |       |     |\n  //       |     out2    out3  out4\n  //       |      |       |\n  //      =======phony======\n  //              |\n  //             all\n  //\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n                                      \"build out1: cat in1\\n\"\n                                      \"build mid1: cat in1\\n\"\n                                      \"build out2: cat mid1\\n\"\n                                      \"build out3 out4: cat mid1\\n\"\n                                      \"build all: phony out1 out2 out3\\n\"));\n\n  InputsCollector collector;\n\n  // Start visit from out1, this should add in1 to the inputs.\n  collector.Reset();\n  collector.VisitNode(GetNode(\"out1\"));\n  auto inputs = collector.GetInputsAsStrings();\n  ASSERT_EQ(1u, inputs.size());\n  EXPECT_EQ(\"in1\", inputs[0]);\n\n  // Add a visit from out2, this should add mid1.\n  collector.VisitNode(GetNode(\"out2\"));\n  inputs = collector.GetInputsAsStrings();\n  ASSERT_EQ(2u, inputs.size());\n  EXPECT_EQ(\"in1\", inputs[0]);\n  EXPECT_EQ(\"mid1\", inputs[1]);\n\n  // Another visit from all, this should add out1, out2 and out3,\n  // but not out4.\n  collector.VisitNode(GetNode(\"all\"));\n  inputs = collector.GetInputsAsStrings();\n  ASSERT_EQ(5u, inputs.size());\n  EXPECT_EQ(\"in1\", inputs[0]);\n  EXPECT_EQ(\"mid1\", inputs[1]);\n  EXPECT_EQ(\"out1\", inputs[2]);\n  EXPECT_EQ(\"out2\", inputs[3]);\n  EXPECT_EQ(\"out3\", inputs[4]);\n\n  collector.Reset();\n\n  // Starting directly from all, will add out1 before mid1 compared\n  // to the previous example above.\n  collector.VisitNode(GetNode(\"all\"));\n  inputs = collector.GetInputsAsStrings();\n  ASSERT_EQ(5u, inputs.size());\n  EXPECT_EQ(\"in1\", inputs[0]);\n  EXPECT_EQ(\"out1\", inputs[1]);\n  EXPECT_EQ(\"mid1\", inputs[2]);\n  EXPECT_EQ(\"out2\", inputs[3]);\n  EXPECT_EQ(\"out3\", inputs[4]);\n}\n\nTEST_F(GraphTest, InputsCollectorWithEscapes) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n      &state_,\n      \"build out$ 1: cat in1 in2 in$ with$ space | implicit || order_only\\n\"));\n\n  InputsCollector collector;\n  collector.VisitNode(GetNode(\"out 1\"));\n  auto inputs = collector.GetInputsAsStrings();\n  ASSERT_EQ(5u, inputs.size());\n  EXPECT_EQ(\"in1\", inputs[0]);\n  EXPECT_EQ(\"in2\", inputs[1]);\n  EXPECT_EQ(\"in with space\", inputs[2]);\n  EXPECT_EQ(\"implicit\", inputs[3]);\n  EXPECT_EQ(\"order_only\", inputs[4]);\n\n  inputs = collector.GetInputsAsStrings(true);\n  ASSERT_EQ(5u, inputs.size());\n  EXPECT_EQ(\"in1\", inputs[0]);\n  EXPECT_EQ(\"in2\", inputs[1]);\n#ifdef _WIN32\n  EXPECT_EQ(\"\\\"in with space\\\"\", inputs[2]);\n#else\n  EXPECT_EQ(\"'in with space'\", inputs[2]);\n#endif\n  EXPECT_EQ(\"implicit\", inputs[3]);\n  EXPECT_EQ(\"order_only\", inputs[4]);\n}\n\nTEST_F(GraphTest, CommandCollector) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n                                      \"build out1: cat in1\\n\"\n                                      \"build mid1: cat in1\\n\"\n                                      \"build out2: cat mid1\\n\"\n                                      \"build out3 out4: cat mid1\\n\"\n                                      \"build all: phony out1 out2 out3\\n\"));\n  {\n    CommandCollector collector;\n    auto& edges = collector.in_edges;\n\n    // Start visit from out2; this should add `build mid1` and `build out2` to\n    // the edge list.\n    collector.CollectFrom(GetNode(\"out2\"));\n    ASSERT_EQ(2u, edges.size());\n    EXPECT_EQ(\"cat in1 > mid1\", edges[0]->EvaluateCommand());\n    EXPECT_EQ(\"cat mid1 > out2\", edges[1]->EvaluateCommand());\n\n    // Add a visit from out1, this should append `build out1`\n    collector.CollectFrom(GetNode(\"out1\"));\n    ASSERT_EQ(3u, edges.size());\n    EXPECT_EQ(\"cat in1 > out1\", edges[2]->EvaluateCommand());\n\n    // Another visit from all; this should add edges for out1, out2 and out3,\n    // but not all (because it's phony).\n    collector.CollectFrom(GetNode(\"all\"));\n    ASSERT_EQ(4u, edges.size());\n    EXPECT_EQ(\"cat in1 > mid1\", edges[0]->EvaluateCommand());\n    EXPECT_EQ(\"cat mid1 > out2\", edges[1]->EvaluateCommand());\n    EXPECT_EQ(\"cat in1 > out1\", edges[2]->EvaluateCommand());\n    EXPECT_EQ(\"cat mid1 > out3 out4\", edges[3]->EvaluateCommand());\n  }\n\n  {\n    CommandCollector collector;\n    auto& edges = collector.in_edges;\n\n    // Starting directly from all, will add `build out1` before `build mid1`\n    // compared to the previous example above.\n    collector.CollectFrom(GetNode(\"all\"));\n    ASSERT_EQ(4u, edges.size());\n    EXPECT_EQ(\"cat in1 > out1\", edges[0]->EvaluateCommand());\n    EXPECT_EQ(\"cat in1 > mid1\", edges[1]->EvaluateCommand());\n    EXPECT_EQ(\"cat mid1 > out2\", edges[2]->EvaluateCommand());\n    EXPECT_EQ(\"cat mid1 > out3 out4\", edges[3]->EvaluateCommand());\n  }\n}\n\nTEST_F(GraphTest, VarInOutPathEscaping) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build a$ b: cat no'space with$ space$$ no\\\"space2\\n\"));\n\n  Edge* edge = GetNode(\"a b\")->in_edge();\n#ifdef _WIN32\n  EXPECT_EQ(\"cat no'space \\\"with space$\\\" \\\"no\\\\\\\"space2\\\" > \\\"a b\\\"\",\n      edge->EvaluateCommand());\n#else\n  EXPECT_EQ(\"cat 'no'\\\\''space' 'with space$' 'no\\\"space2' > 'a b'\",\n      edge->EvaluateCommand());\n#endif\n}\n\n// Regression test for https://github.com/ninja-build/ninja/issues/380\nTEST_F(GraphTest, DepfileWithCanonicalizablePath) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule catdep\\n\"\n\"  depfile = $out.d\\n\"\n\"  command = cat $in > $out\\n\"\n\"build ./out.o: catdep ./foo.cc\\n\"));\n  fs_.Create(\"foo.cc\", \"\");\n  fs_.Create(\"out.o.d\", \"out.o: bar/../foo.cc\\n\");\n  fs_.Create(\"out.o\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.o\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_FALSE(GetNode(\"out.o\")->dirty());\n}\n\n// Regression test for https://github.com/ninja-build/ninja/issues/404\nTEST_F(GraphTest, DepfileRemoved) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule catdep\\n\"\n\"  depfile = $out.d\\n\"\n\"  command = cat $in > $out\\n\"\n\"build ./out.o: catdep ./foo.cc\\n\"));\n  fs_.Create(\"foo.h\", \"\");\n  fs_.Create(\"foo.cc\", \"\");\n  fs_.Tick();\n  fs_.Create(\"out.o.d\", \"out.o: foo.h\\n\");\n  fs_.Create(\"out.o\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.o\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_FALSE(GetNode(\"out.o\")->dirty());\n\n  state_.Reset();\n  fs_.RemoveFile(\"out.o.d\");\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out.o\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n  EXPECT_TRUE(GetNode(\"out.o\")->dirty());\n}\n\n// Check that rule-level variables are in scope for eval.\nTEST_F(GraphTest, RuleVariablesInScope) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule r\\n\"\n\"  depfile = x\\n\"\n\"  command = depfile is $depfile\\n\"\n\"build out: r in\\n\"));\n  Edge* edge = GetNode(\"out\")->in_edge();\n  EXPECT_EQ(\"depfile is x\", edge->EvaluateCommand());\n}\n\n// Check that build statements can override rule builtins like depfile.\nTEST_F(GraphTest, DepfileOverride) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule r\\n\"\n\"  depfile = x\\n\"\n\"  command = unused\\n\"\n\"build out: r in\\n\"\n\"  depfile = y\\n\"));\n  Edge* edge = GetNode(\"out\")->in_edge();\n  EXPECT_EQ(\"y\", edge->GetBinding(\"depfile\"));\n}\n\n// Check that overridden values show up in expansion of rule-level bindings.\nTEST_F(GraphTest, DepfileOverrideParent) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule r\\n\"\n\"  depfile = x\\n\"\n\"  command = depfile is $depfile\\n\"\n\"build out: r in\\n\"\n\"  depfile = y\\n\"));\n  Edge* edge = GetNode(\"out\")->in_edge();\n  EXPECT_EQ(\"depfile is y\", edge->GetBinding(\"command\"));\n}\n\n// Verify that building a nested phony rule prints \"no work to do\"\nTEST_F(GraphTest, NestedPhonyPrintsDone) {\n  AssertParse(&state_,\n\"build n1: phony \\n\"\n\"build n2: phony n1\\n\"\n  );\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"n2\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  Plan plan_;\n  EXPECT_TRUE(plan_.AddTarget(GetNode(\"n2\"), &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_EQ(0, plan_.command_edge_count());\n  ASSERT_FALSE(plan_.more_to_do());\n}\n\nTEST_F(GraphTest, PhonySelfReferenceError) {\n  ManifestParserOptions parser_opts;\n  parser_opts.phony_cycle_action_ = kPhonyCycleActionError;\n  AssertParse(&state_,\n\"build a: phony a\\n\",\n  parser_opts);\n\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"a\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: a -> a [-w phonycycle=err]\", err);\n}\n\nTEST_F(GraphTest, DependencyCycle) {\n  AssertParse(&state_,\n\"build out: cat mid\\n\"\n\"build mid: cat in\\n\"\n\"build in: cat pre\\n\"\n\"build pre: cat out\\n\");\n\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: out -> mid -> in -> pre -> out\", err);\n}\n\nTEST_F(GraphTest, CycleInEdgesButNotInNodes1) {\n  string err;\n  AssertParse(&state_,\n\"build a b: cat a\\n\");\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"b\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: a -> a\", err);\n}\n\nTEST_F(GraphTest, CycleInEdgesButNotInNodes2) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build b a: cat a\\n\"));\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"b\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: a -> a\", err);\n}\n\nTEST_F(GraphTest, CycleInEdgesButNotInNodes3) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build a b: cat c\\n\"\n\"build c: cat a\\n\"));\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"b\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: a -> c -> a\", err);\n}\n\nTEST_F(GraphTest, CycleInEdgesButNotInNodes4) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build d: cat c\\n\"\n\"build c: cat b\\n\"\n\"build b: cat a\\n\"\n\"build a e: cat d\\n\"\n\"build f: cat e\\n\"));\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"f\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: a -> d -> c -> b -> a\", err);\n}\n\n// Verify that cycles in graphs with multiple outputs are handled correctly\n// in RecomputeDirty() and don't cause deps to be loaded multiple times.\nTEST_F(GraphTest, CycleWithLengthZeroFromDepfile) {\n  AssertParse(&state_,\n\"rule deprule\\n\"\n\"   depfile = dep.d\\n\"\n\"   command = unused\\n\"\n\"build a b: deprule\\n\"\n  );\n  fs_.Create(\"dep.d\", \"a: b\\n\");\n\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"a\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: b -> b\", err);\n\n  // Despite the depfile causing edge to be a cycle (it has outputs a and b,\n  // but the depfile also adds b as an input), the deps should have been loaded\n  // only once:\n  Edge* edge = GetNode(\"a\")->in_edge();\n  EXPECT_EQ(size_t(1), edge->inputs_.size());\n  EXPECT_EQ(\"b\", edge->inputs_[0]->path());\n}\n\n// Like CycleWithLengthZeroFromDepfile but with a higher cycle length.\nTEST_F(GraphTest, CycleWithLengthOneFromDepfile) {\n  AssertParse(&state_,\n\"rule deprule\\n\"\n\"   depfile = dep.d\\n\"\n\"   command = unused\\n\"\n\"rule r\\n\"\n\"   command = unused\\n\"\n\"build a b: deprule\\n\"\n\"build c: r b\\n\"\n  );\n  fs_.Create(\"dep.d\", \"a: c\\n\");\n\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"a\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: b -> c -> b\", err);\n\n  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,\n  // but c's in_edge has b as input but the depfile also adds |edge| as\n  // output)), the deps should have been loaded only once:\n  Edge* edge = GetNode(\"a\")->in_edge();\n  EXPECT_EQ(size_t(1), edge->inputs_.size());\n  EXPECT_EQ(\"c\", edge->inputs_[0]->path());\n}\n\n// Like CycleWithLengthOneFromDepfile but building a node one hop away from\n// the cycle.\nTEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) {\n  AssertParse(&state_,\n\"rule deprule\\n\"\n\"   depfile = dep.d\\n\"\n\"   command = unused\\n\"\n\"rule r\\n\"\n\"   command = unused\\n\"\n\"build a b: deprule\\n\"\n\"build c: r b\\n\"\n\"build d: r a\\n\"\n  );\n  fs_.Create(\"dep.d\", \"a: c\\n\");\n\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"d\"), NULL, &err));\n  ASSERT_EQ(\"dependency cycle: b -> c -> b\", err);\n\n  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,\n  // but c's in_edge has b as input but the depfile also adds |edge| as\n  // output)), the deps should have been loaded only once:\n  Edge* edge = GetNode(\"a\")->in_edge();\n  EXPECT_EQ(size_t(1), edge->inputs_.size());\n  EXPECT_EQ(\"c\", edge->inputs_[0]->path());\n}\n\n#ifdef _WIN32\nTEST_F(GraphTest, Decanonicalize) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out\\\\out1: cat src\\\\in1\\n\"\n\"build out\\\\out2/out3\\\\out4: cat mid1\\n\"\n\"build out3 out4\\\\foo: cat mid1\\n\"));\n\n  string err;\n  vector<Node*> root_nodes = state_.RootNodes(&err);\n  EXPECT_EQ(4u, root_nodes.size());\n  EXPECT_EQ(root_nodes[0]->path(), \"out/out1\");\n  EXPECT_EQ(root_nodes[1]->path(), \"out/out2/out3/out4\");\n  EXPECT_EQ(root_nodes[2]->path(), \"out3\");\n  EXPECT_EQ(root_nodes[3]->path(), \"out4/foo\");\n  EXPECT_EQ(root_nodes[0]->PathDecanonicalized(), \"out\\\\out1\");\n  EXPECT_EQ(root_nodes[1]->PathDecanonicalized(), \"out\\\\out2/out3\\\\out4\");\n  EXPECT_EQ(root_nodes[2]->PathDecanonicalized(), \"out3\");\n  EXPECT_EQ(root_nodes[3]->PathDecanonicalized(), \"out4\\\\foo\");\n}\n#endif\n\nTEST_F(GraphTest, DyndepLoadTrivial) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r in || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd\")->dyndep_pending());\n  EXPECT_TRUE(scan_.LoadDyndeps(GetNode(\"dd\"), &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_FALSE(GetNode(\"dd\")->dyndep_pending());\n\n  Edge* edge = GetNode(\"out\")->in_edge();\n  ASSERT_EQ(size_t(1), edge->outputs_.size());\n  EXPECT_EQ(\"out\", edge->outputs_[0]->path());\n  ASSERT_EQ(size_t(2), edge->inputs_.size());\n  EXPECT_EQ(\"in\", edge->inputs_[0]->path());\n  EXPECT_EQ(\"dd\", edge->inputs_[1]->path());\n  EXPECT_EQ(0, edge->implicit_deps_);\n  EXPECT_EQ(1, edge->order_only_deps_);\n  EXPECT_FALSE(edge->GetBindingBool(\"restat\"));\n}\n\nTEST_F(GraphTest, DyndepLoadImplicit) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out1: r in || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out2: r in\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out1: dyndep | out2\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd\")->dyndep_pending());\n  EXPECT_TRUE(scan_.LoadDyndeps(GetNode(\"dd\"), &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_FALSE(GetNode(\"dd\")->dyndep_pending());\n\n  Edge* edge = GetNode(\"out1\")->in_edge();\n  ASSERT_EQ(size_t(1), edge->outputs_.size());\n  EXPECT_EQ(\"out1\", edge->outputs_[0]->path());\n  ASSERT_EQ(size_t(3), edge->inputs_.size());\n  EXPECT_EQ(\"in\", edge->inputs_[0]->path());\n  EXPECT_EQ(\"out2\", edge->inputs_[1]->path());\n  EXPECT_EQ(\"dd\", edge->inputs_[2]->path());\n  EXPECT_EQ(1, edge->implicit_deps_);\n  EXPECT_EQ(1, edge->order_only_deps_);\n  EXPECT_FALSE(edge->GetBindingBool(\"restat\"));\n}\n\nTEST_F(GraphTest, DyndepLoadMissingFile) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r in || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd\")->dyndep_pending());\n  EXPECT_FALSE(scan_.LoadDyndeps(GetNode(\"dd\"), &err));\n  EXPECT_EQ(\"loading 'dd': No such file or directory\", err);\n}\n\nTEST_F(GraphTest, DyndepLoadMissingEntry) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r in || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd\")->dyndep_pending());\n  EXPECT_FALSE(scan_.LoadDyndeps(GetNode(\"dd\"), &err));\n  EXPECT_EQ(\"'out' not mentioned in its dyndep file 'dd'\", err);\n}\n\nTEST_F(GraphTest, DyndepLoadExtraEntry) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r in || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out2: r in || dd\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep\\n\"\n\"build out2: dyndep\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd\")->dyndep_pending());\n  EXPECT_FALSE(scan_.LoadDyndeps(GetNode(\"dd\"), &err));\n  EXPECT_EQ(\"dyndep file 'dd' mentions output 'out2' whose build statement \"\n            \"does not have a dyndep binding for the file\", err);\n}\n\nTEST_F(GraphTest, DyndepLoadOutputWithMultipleRules1) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out1 | out-twice.imp: r in1\\n\"\n\"build out2: r in2 || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out2 | out-twice.imp: dyndep\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd\")->dyndep_pending());\n  EXPECT_FALSE(scan_.LoadDyndeps(GetNode(\"dd\"), &err));\n  EXPECT_EQ(\"multiple rules generate out-twice.imp\", err);\n}\n\nTEST_F(GraphTest, DyndepLoadOutputWithMultipleRules2) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out1: r in1 || dd1\\n\"\n\"  dyndep = dd1\\n\"\n\"build out2: r in2 || dd2\\n\"\n\"  dyndep = dd2\\n\"\n  );\n  fs_.Create(\"dd1\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out1 | out-twice.imp: dyndep\\n\"\n  );\n  fs_.Create(\"dd2\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out2 | out-twice.imp: dyndep\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd1\")->dyndep_pending());\n  EXPECT_TRUE(scan_.LoadDyndeps(GetNode(\"dd1\"), &err));\n  EXPECT_EQ(\"\", err);\n  ASSERT_TRUE(GetNode(\"dd2\")->dyndep_pending());\n  EXPECT_FALSE(scan_.LoadDyndeps(GetNode(\"dd2\"), &err));\n  EXPECT_EQ(\"multiple rules generate out-twice.imp\", err);\n}\n\nTEST_F(GraphTest, DyndepLoadMultiple) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out1: r in1 || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build out2: r in2 || dd\\n\"\n\"  dyndep = dd\\n\"\n\"build outNot: r in3 || dd\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out1 | out1imp: dyndep | in1imp\\n\"\n\"build out2: dyndep | in2imp\\n\"\n\"  restat = 1\\n\"\n  );\n\n  string err;\n  ASSERT_TRUE(GetNode(\"dd\")->dyndep_pending());\n  EXPECT_TRUE(scan_.LoadDyndeps(GetNode(\"dd\"), &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_FALSE(GetNode(\"dd\")->dyndep_pending());\n\n  Edge* edge1 = GetNode(\"out1\")->in_edge();\n  ASSERT_EQ(size_t(2), edge1->outputs_.size());\n  EXPECT_EQ(\"out1\", edge1->outputs_[0]->path());\n  EXPECT_EQ(\"out1imp\", edge1->outputs_[1]->path());\n  EXPECT_EQ(1, edge1->implicit_outs_);\n  ASSERT_EQ(size_t(3), edge1->inputs_.size());\n  EXPECT_EQ(\"in1\", edge1->inputs_[0]->path());\n  EXPECT_EQ(\"in1imp\", edge1->inputs_[1]->path());\n  EXPECT_EQ(\"dd\", edge1->inputs_[2]->path());\n  EXPECT_EQ(1, edge1->implicit_deps_);\n  EXPECT_EQ(1, edge1->order_only_deps_);\n  EXPECT_FALSE(edge1->GetBindingBool(\"restat\"));\n  EXPECT_EQ(edge1, GetNode(\"out1imp\")->in_edge());\n  Node* in1imp = GetNode(\"in1imp\");\n  ASSERT_EQ(size_t(1), in1imp->out_edges().size());\n  EXPECT_EQ(edge1, in1imp->out_edges()[0]);\n\n  Edge* edge2 = GetNode(\"out2\")->in_edge();\n  ASSERT_EQ(size_t(1), edge2->outputs_.size());\n  EXPECT_EQ(\"out2\", edge2->outputs_[0]->path());\n  EXPECT_EQ(0, edge2->implicit_outs_);\n  ASSERT_EQ(size_t(3), edge2->inputs_.size());\n  EXPECT_EQ(\"in2\", edge2->inputs_[0]->path());\n  EXPECT_EQ(\"in2imp\", edge2->inputs_[1]->path());\n  EXPECT_EQ(\"dd\", edge2->inputs_[2]->path());\n  EXPECT_EQ(1, edge2->implicit_deps_);\n  EXPECT_EQ(1, edge2->order_only_deps_);\n  EXPECT_TRUE(edge2->GetBindingBool(\"restat\"));\n  Node* in2imp = GetNode(\"in2imp\");\n  ASSERT_EQ(size_t(1), in2imp->out_edges().size());\n  EXPECT_EQ(edge2, in2imp->out_edges()[0]);\n}\n\nTEST_F(GraphTest, DyndepFileMissing) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"loading 'dd': No such file or directory\", err);\n}\n\nTEST_F(GraphTest, DyndepFileError) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n  );\n\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"'out' not mentioned in its dyndep file 'dd'\", err);\n}\n\nTEST_F(GraphTest, DyndepImplicitInputNewer) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | in\\n\"\n  );\n  fs_.Create(\"out\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_FALSE(GetNode(\"in\")->dirty());\n  EXPECT_FALSE(GetNode(\"dd\")->dirty());\n\n  // \"out\" is dirty due to dyndep-specified implicit input\n  EXPECT_TRUE(GetNode(\"out\")->dirty());\n}\n\nTEST_F(GraphTest, DyndepFileReady) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build dd: r dd-in\\n\"\n\"build out: r || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd-in\", \"\");\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out: dyndep | in\\n\"\n  );\n  fs_.Create(\"out\", \"\");\n  fs_.Tick();\n  fs_.Create(\"in\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_FALSE(GetNode(\"in\")->dirty());\n  EXPECT_FALSE(GetNode(\"dd\")->dirty());\n  EXPECT_TRUE(GetNode(\"dd\")->in_edge()->outputs_ready());\n\n  // \"out\" is dirty due to dyndep-specified implicit input\n  EXPECT_TRUE(GetNode(\"out\")->dirty());\n}\n\nTEST_F(GraphTest, DyndepFileNotClean) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build dd: r dd-in\\n\"\n\"build out: r || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd\", \"this-should-not-be-loaded\");\n  fs_.Tick();\n  fs_.Create(\"dd-in\", \"\");\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_TRUE(GetNode(\"dd\")->dirty());\n  EXPECT_FALSE(GetNode(\"dd\")->in_edge()->outputs_ready());\n\n  // \"out\" is clean but not ready since \"dd\" is not ready\n  EXPECT_FALSE(GetNode(\"out\")->dirty());\n  EXPECT_FALSE(GetNode(\"out\")->in_edge()->outputs_ready());\n}\n\nTEST_F(GraphTest, DyndepFileNotReady) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build tmp: r\\n\"\n\"build dd: r dd-in || tmp\\n\"\n\"build out: r || dd\\n\"\n\"  dyndep = dd\\n\"\n  );\n  fs_.Create(\"dd\", \"this-should-not-be-loaded\");\n  fs_.Create(\"dd-in\", \"\");\n  fs_.Tick();\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_FALSE(GetNode(\"dd\")->dirty());\n  EXPECT_FALSE(GetNode(\"dd\")->in_edge()->outputs_ready());\n  EXPECT_FALSE(GetNode(\"out\")->dirty());\n  EXPECT_FALSE(GetNode(\"out\")->in_edge()->outputs_ready());\n}\n\nTEST_F(GraphTest, DyndepFileSecondNotReady) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build dd1: r dd1-in\\n\"\n\"build dd2-in: r || dd1\\n\"\n\"  dyndep = dd1\\n\"\n\"build dd2: r dd2-in\\n\"\n\"build out: r || dd2\\n\"\n\"  dyndep = dd2\\n\"\n  );\n  fs_.Create(\"dd1\", \"\");\n  fs_.Create(\"dd2\", \"\");\n  fs_.Create(\"dd2-in\", \"\");\n  fs_.Tick();\n  fs_.Create(\"dd1-in\", \"\");\n  fs_.Create(\"out\", \"\");\n\n  string err;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  ASSERT_EQ(\"\", err);\n\n  EXPECT_TRUE(GetNode(\"dd1\")->dirty());\n  EXPECT_FALSE(GetNode(\"dd1\")->in_edge()->outputs_ready());\n  EXPECT_FALSE(GetNode(\"dd2\")->dirty());\n  EXPECT_FALSE(GetNode(\"dd2\")->in_edge()->outputs_ready());\n  EXPECT_FALSE(GetNode(\"out\")->dirty());\n  EXPECT_FALSE(GetNode(\"out\")->in_edge()->outputs_ready());\n}\n\nTEST_F(GraphTest, DyndepFileCircular) {\n  AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out: r in || dd\\n\"\n\"  depfile = out.d\\n\"\n\"  dyndep = dd\\n\"\n\"build in: r circ\\n\"\n  );\n  fs_.Create(\"out.d\", \"out: inimp\\n\");\n  fs_.Create(\"dd\",\n\"ninja_dyndep_version = 1\\n\"\n\"build out | circ: dyndep\\n\"\n  );\n  fs_.Create(\"out\", \"\");\n\n  Edge* edge = GetNode(\"out\")->in_edge();\n  string err;\n  EXPECT_FALSE(scan_.RecomputeDirty(GetNode(\"out\"), NULL, &err));\n  EXPECT_EQ(\"dependency cycle: circ -> in -> circ\", err);\n\n  // Verify that \"out.d\" was loaded exactly once despite\n  // circular reference discovered from dyndep file.\n  ASSERT_EQ(size_t(3), edge->inputs_.size());\n  EXPECT_EQ(\"in\", edge->inputs_[0]->path());\n  EXPECT_EQ(\"inimp\", edge->inputs_[1]->path());\n  EXPECT_EQ(\"dd\", edge->inputs_[2]->path());\n  EXPECT_EQ(1, edge->implicit_deps_);\n  EXPECT_EQ(1, edge->order_only_deps_);\n}\n\nTEST_F(GraphTest, Validation) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"build out: cat in |@ validate\\n\"\n\"build validate: cat in\\n\"));\n\n  fs_.Create(\"in\", \"\");\n  string err;\n  std::vector<Node*> validation_nodes;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), &validation_nodes, &err));\n  ASSERT_EQ(\"\", err);\n\n  ASSERT_EQ(validation_nodes.size(), size_t(1));\n  EXPECT_EQ(validation_nodes[0]->path(), \"validate\");\n\n  EXPECT_TRUE(GetNode(\"out\")->dirty());\n  EXPECT_TRUE(GetNode(\"validate\")->dirty());\n}\n\n// Check that phony's dependencies' mtimes are propagated.\nTEST_F(GraphTest, PhonyDepsMtimes) {\n  string err;\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule touch\\n\"\n\" command = touch $out\\n\"\n\"build in_ph: phony in1\\n\"\n\"build out1: touch in_ph\\n\"\n));\n  fs_.Create(\"in1\", \"\");\n  fs_.Create(\"out1\", \"\");\n  Node* out1 = GetNode(\"out1\");\n  Node* in1  = GetNode(\"in1\");\n\n  EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err));\n  EXPECT_TRUE(!out1->dirty());\n\n  // Get the mtime of out1\n  ASSERT_TRUE(in1->Stat(&fs_, &err));\n  ASSERT_TRUE(out1->Stat(&fs_, &err));\n  TimeStamp out1Mtime1 = out1->mtime();\n  TimeStamp in1Mtime1 = in1->mtime();\n\n  // Touch in1. This should cause out1 to be dirty\n  state_.Reset();\n  fs_.Tick();\n  fs_.Create(\"in1\", \"\");\n\n  ASSERT_TRUE(in1->Stat(&fs_, &err));\n  EXPECT_GT(in1->mtime(), in1Mtime1);\n\n  EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err));\n  EXPECT_GT(in1->mtime(), in1Mtime1);\n  EXPECT_EQ(out1->mtime(), out1Mtime1);\n  EXPECT_TRUE(out1->dirty());\n}\n\n// Test that EdgeQueue correctly prioritizes by critical time\nTEST_F(GraphTest, EdgeQueuePriority) {\n\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n\"rule r\\n\"\n\"  command = unused\\n\"\n\"build out1: r in1\\n\"\n\"build out2: r in2\\n\"\n\"build out3: r in3\\n\"\n));\n\n  const int n_edges = 3;\n  Edge *(edges)[n_edges] = {\n    GetNode(\"out1\")->in_edge(),\n    GetNode(\"out2\")->in_edge(),\n    GetNode(\"out3\")->in_edge(),\n  };\n\n  // Output is largest critical time to smallest\n  for (int i = 0; i < n_edges; ++i) {\n    edges[i]->set_critical_path_weight(i * 10);\n  }\n\n  EdgePriorityQueue queue;\n  for (int i = 0; i < n_edges; ++i) {\n    queue.push(edges[i]);\n  }\n\n  EXPECT_EQ(queue.size(), static_cast<size_t>(n_edges));\n  for (int i = 0; i < n_edges; ++i) {\n    EXPECT_EQ(queue.top(), edges[n_edges - 1 - i]);\n    queue.pop();\n  }\n  EXPECT_TRUE(queue.empty());\n\n  // When there is ambiguity, the lowest edge id comes first\n  for (int i = 0; i < n_edges; ++i) {\n    edges[i]->set_critical_path_weight(0);\n  }\n\n  queue.push(edges[1]);\n  queue.push(edges[2]);\n  queue.push(edges[0]);\n\n  for (int i = 0; i < n_edges; ++i) {\n    EXPECT_EQ(queue.top(), edges[i]);\n    queue.pop();\n  }\n  EXPECT_TRUE(queue.empty());\n}\n\nTEST_F(GraphTest, PhonyOutputWithValidation) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,\n                                      \"build valid: phony\\n\"\n                                      \"build out: phony |@ valid\\n\"));\n  fs_.Create(\"valid\", \"\");\n\n  string err;\n  std::vector<Node*> validation_nodes;\n  EXPECT_TRUE(scan_.RecomputeDirty(GetNode(\"out\"), &validation_nodes, &err));\n  ASSERT_EQ(\"\", err);\n\n  // Phony output with validation should not be dirty even if output is missing.\n  EXPECT_FALSE(GetNode(\"out\")->dirty());\n  ASSERT_EQ(1u, validation_nodes.size());\n  EXPECT_EQ(\"valid\", validation_nodes[0]->path());\n}\n"
  },
  {
    "path": "src/graphviz.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"graphviz.h\"\n\n#include <stdio.h>\n#include <algorithm>\n\n#include \"dyndep.h\"\n#include \"graph.h\"\n\nusing namespace std;\n\nvoid GraphViz::AddTarget(Node* node) {\n  if (visited_nodes_.find(node) != visited_nodes_.end())\n    return;\n\n  string pathstr = node->path();\n  replace(pathstr.begin(), pathstr.end(), '\\\\', '/');\n  printf(\"\\\"%p\\\" [label=\\\"%s\\\"]\\n\", node, pathstr.c_str());\n  visited_nodes_.insert(node);\n\n  Edge* edge = node->in_edge();\n\n  if (!edge) {\n    // Leaf node.\n    // Draw as a rect?\n    return;\n  }\n\n  if (visited_edges_.find(edge) != visited_edges_.end())\n    return;\n  visited_edges_.insert(edge);\n\n  if (edge->dyndep_ && edge->dyndep_->dyndep_pending()) {\n    std::string err;\n    if (!dyndep_loader_.LoadDyndeps(edge->dyndep_, &err)) {\n      Warning(\"%s\\n\", err.c_str());\n    }\n  }\n\n  if (edge->inputs_.size() == 1 && edge->outputs_.size() == 1) {\n    // Can draw simply.\n    // Note extra space before label text -- this is cosmetic and feels\n    // like a graphviz bug.\n    printf(\"\\\"%p\\\" -> \\\"%p\\\" [label=\\\" %s\\\"]\\n\",\n           edge->inputs_[0], edge->outputs_[0], edge->rule_->name().c_str());\n  } else {\n    printf(\"\\\"%p\\\" [label=\\\"%s\\\", shape=ellipse]\\n\",\n           edge, edge->rule_->name().c_str());\n    for (vector<Node*>::iterator out = edge->outputs_.begin();\n         out != edge->outputs_.end(); ++out) {\n      printf(\"\\\"%p\\\" -> \\\"%p\\\"\\n\", edge, *out);\n    }\n    for (vector<Node*>::iterator in = edge->inputs_.begin();\n         in != edge->inputs_.end(); ++in) {\n      const char* order_only = \"\";\n      if (edge->is_order_only(in - edge->inputs_.begin()))\n        order_only = \" style=dotted\";\n      printf(\"\\\"%p\\\" -> \\\"%p\\\" [arrowhead=none%s]\\n\", (*in), edge, order_only);\n    }\n  }\n\n  for (vector<Node*>::iterator in = edge->inputs_.begin();\n       in != edge->inputs_.end(); ++in) {\n    AddTarget(*in);\n  }\n}\n\nvoid GraphViz::Start() {\n  printf(\"digraph ninja {\\n\");\n  printf(\"rankdir=\\\"LR\\\"\\n\");\n  printf(\"node [fontsize=10, shape=box, height=0.25]\\n\");\n  printf(\"edge [fontsize=10]\\n\");\n}\n\nvoid GraphViz::Finish() {\n  printf(\"}\\n\");\n}\n"
  },
  {
    "path": "src/graphviz.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_GRAPHVIZ_H_\n#define NINJA_GRAPHVIZ_H_\n\n#include <set>\n\n#include \"dyndep.h\"\n#include \"graph.h\"\n\nstruct DiskInterface;\nstruct Node;\nstruct Edge;\nstruct State;\n\n/// Runs the process of creating GraphViz .dot file output.\nstruct GraphViz {\n  GraphViz(State* state, DiskInterface* disk_interface)\n      : dyndep_loader_(state, disk_interface) {}\n  void Start();\n  void AddTarget(Node* node);\n  void Finish();\n\n  DyndepLoader dyndep_loader_;\n  std::set<Node*> visited_nodes_;\n  EdgeSet visited_edges_;\n};\n\n#endif  // NINJA_GRAPHVIZ_H_\n"
  },
  {
    "path": "src/hash_collision_bench.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"build_log.h\"\n\n#include <algorithm>\n\n#include <stdlib.h>\n#include <time.h>\n\nusing namespace std;\n\nint random(int low, int high) {\n  return int(low + (rand() / double(RAND_MAX)) * (high - low) + 0.5);\n}\n\nvoid RandomCommand(char** s) {\n  int len = random(5, 100);\n  *s = new char[len+1];\n  for (int i = 0; i < len; ++i)\n    (*s)[i] = (char)random(32, 127);\n  (*s)[len] = '\\0';\n}\n\nint main() {\n  const int N = 20 * 1000 * 1000;\n\n  // Leak these, else 10% of the runtime is spent destroying strings.\n  char** commands = new char*[N];\n  pair<uint64_t, int>* hashes = new pair<uint64_t, int>[N];\n\n  srand((int)time(NULL));\n\n  for (int i = 0; i < N; ++i) {\n    RandomCommand(&commands[i]);\n    hashes[i] = make_pair(BuildLog::LogEntry::HashCommand(commands[i]), i);\n  }\n\n  sort(hashes, hashes + N);\n\n  int collision_count = 0;\n  for (int i = 1; i < N; ++i) {\n    if (hashes[i - 1].first == hashes[i].first) {\n      if (strcmp(commands[hashes[i - 1].second],\n                 commands[hashes[i].second]) != 0) {\n        printf(\"collision!\\n  string 1: '%s'\\n  string 2: '%s'\\n\",\n               commands[hashes[i - 1].second],\n               commands[hashes[i].second]);\n        collision_count++;\n      }\n    }\n  }\n  printf(\"\\n\\n%d collisions after %d runs\\n\", collision_count, N);\n}\n"
  },
  {
    "path": "src/hash_map.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_MAP_H_\n#define NINJA_MAP_H_\n\n#include <algorithm>\n#include <string.h>\n#include \"string_piece.h\"\n#include \"util.h\"\n\n#include \"third_party/emhash/hash_table8.hpp\"\n#include \"third_party/rapidhash/rapidhash.h\"\n\nnamespace std {\ntemplate<>\nstruct hash<StringPiece> {\n  typedef StringPiece argument_type;\n  typedef size_t result_type;\n\n  size_t operator()(StringPiece key) const {\n    return rapidhash(key.str_, key.len_);\n  }\n};\n}\n\n/// A template for hash_maps keyed by a StringPiece whose string is\n/// owned externally (typically by the values).  Use like:\n/// ExternalStringHash<Foo*>::Type foos; to make foos into a hash\n/// mapping StringPiece => Foo*.\ntemplate<typename V>\nstruct ExternalStringHashMap {\n  typedef emhash8::HashMap<StringPiece, V> Type;\n};\n\n#endif // NINJA_MAP_H_\n"
  },
  {
    "path": "src/includes_normalize-win32.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"includes_normalize.h\"\n\n#include \"string_piece.h\"\n#include \"string_piece_util.h\"\n#include \"util.h\"\n\n#include <algorithm>\n#include <iterator>\n#include <sstream>\n\n#include <windows.h>\n\nusing namespace std;\n\nnamespace {\n\nbool InternalGetFullPathName(const StringPiece& file_name, char* buffer,\n                             size_t buffer_length, string *err) {\n  DWORD result_size = GetFullPathNameA(file_name.AsString().c_str(),\n                                       buffer_length, buffer, NULL);\n  if (result_size == 0) {\n    *err = \"GetFullPathNameA(\" + file_name.AsString() + \"): \" +\n        GetLastErrorString();\n    return false;\n  } else if (result_size > buffer_length) {\n    *err = \"path too long\";\n    return false;\n  }\n  return true;\n}\n\nbool IsPathSeparator(char c) {\n  return c == '/' ||  c == '\\\\';\n}\n\n// Return true if paths a and b are on the same windows drive.\n// Return false if this function cannot check\n// whether or not on the same windows drive.\nbool SameDriveFast(StringPiece a, StringPiece b) {\n  if (a.size() < 3 || b.size() < 3) {\n    return false;\n  }\n\n  if (!islatinalpha(a[0]) || !islatinalpha(b[0])) {\n    return false;\n  }\n\n  if (ToLowerASCII(a[0]) != ToLowerASCII(b[0])) {\n    return false;\n  }\n\n  if (a[1] != ':' || b[1] != ':') {\n    return false;\n  }\n\n  return IsPathSeparator(a[2]) && IsPathSeparator(b[2]);\n}\n\n// Return true if paths a and b are on the same Windows drive.\nbool SameDrive(StringPiece a, StringPiece b, string* err)  {\n  if (SameDriveFast(a, b)) {\n    return true;\n  }\n\n  char a_absolute[_MAX_PATH];\n  char b_absolute[_MAX_PATH];\n  if (!InternalGetFullPathName(a, a_absolute, sizeof(a_absolute), err)) {\n    return false;\n  }\n  if (!InternalGetFullPathName(b, b_absolute, sizeof(b_absolute), err)) {\n    return false;\n  }\n  char a_drive[_MAX_DIR];\n  char b_drive[_MAX_DIR];\n  _splitpath(a_absolute, a_drive, NULL, NULL, NULL);\n  _splitpath(b_absolute, b_drive, NULL, NULL, NULL);\n  return _stricmp(a_drive, b_drive) == 0;\n}\n\n// Check path |s| is FullPath style returned by GetFullPathName.\n// This ignores difference of path separator.\n// This is used not to call very slow GetFullPathName API.\nbool IsFullPathName(StringPiece s) {\n  if (s.size() < 3 ||\n      !islatinalpha(s[0]) ||\n      s[1] != ':' ||\n      !IsPathSeparator(s[2])) {\n    return false;\n  }\n\n  // Check \".\" or \"..\" is contained in path.\n  for (size_t i = 2; i < s.size(); ++i) {\n    if (!IsPathSeparator(s[i])) {\n      continue;\n    }\n\n    // Check \".\".\n    if (i + 1 < s.size() && s[i+1] == '.' &&\n        (i + 2 >= s.size() || IsPathSeparator(s[i+2]))) {\n      return false;\n    }\n\n    // Check \"..\".\n    if (i + 2 < s.size() && s[i+1] == '.' && s[i+2] == '.' &&\n        (i + 3 >= s.size() || IsPathSeparator(s[i+3]))) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n}  // anonymous namespace\n\nIncludesNormalize::IncludesNormalize(const string& relative_to) {\n  string err;\n  relative_to_ = AbsPath(relative_to, &err);\n  if (!err.empty()) {\n    Fatal(\"Initializing IncludesNormalize(): %s\", err.c_str());\n  }\n  split_relative_to_ = SplitStringPiece(relative_to_, '/');\n}\n\nstring IncludesNormalize::AbsPath(StringPiece s, string* err) {\n  if (IsFullPathName(s)) {\n    string result = s.AsString();\n    for (size_t i = 0; i < result.size(); ++i) {\n      if (result[i] == '\\\\') {\n        result[i] = '/';\n      }\n    }\n    return result;\n  }\n\n  char result[_MAX_PATH];\n  if (!InternalGetFullPathName(s, result, sizeof(result), err)) {\n    return \"\";\n  }\n  for (char* c = result; *c; ++c)\n    if (*c == '\\\\')\n      *c = '/';\n  return result;\n}\n\nstring IncludesNormalize::Relativize(\n    StringPiece path, const vector<StringPiece>& start_list, string* err) {\n  string abs_path = AbsPath(path, err);\n  if (!err->empty())\n    return \"\";\n  vector<StringPiece> path_list = SplitStringPiece(abs_path, '/');\n  int i;\n  for (i = 0; i < static_cast<int>(min(start_list.size(), path_list.size()));\n       ++i) {\n    if (!EqualsCaseInsensitiveASCII(start_list[i], path_list[i])) {\n      break;\n    }\n  }\n\n  vector<StringPiece> rel_list;\n  rel_list.reserve(start_list.size() - i + path_list.size() - i);\n  for (int j = 0; j < static_cast<int>(start_list.size() - i); ++j)\n    rel_list.push_back(\"..\");\n  for (int j = i; j < static_cast<int>(path_list.size()); ++j)\n    rel_list.push_back(path_list[j]);\n  if (rel_list.size() == 0)\n    return \".\";\n  return JoinStringPiece(rel_list, '/');\n}\n\nbool IncludesNormalize::Normalize(const string& input,\n                                  string* result, string* err) const {\n  char copy[_MAX_PATH + 1];\n  size_t len = input.size();\n  if (len > _MAX_PATH) {\n    *err = \"path too long\";\n    return false;\n  }\n  strncpy(copy, input.c_str(), input.size() + 1);\n  uint64_t slash_bits;\n  CanonicalizePath(copy, &len, &slash_bits);\n  StringPiece partially_fixed(copy, len);\n  string abs_input = AbsPath(partially_fixed, err);\n  if (!err->empty())\n    return false;\n\n  if (!SameDrive(abs_input, relative_to_, err)) {\n    if (!err->empty())\n      return false;\n    *result = partially_fixed.AsString();\n    return true;\n  }\n  *result = Relativize(abs_input, split_relative_to_, err);\n  if (!err->empty())\n    return false;\n  return true;\n}\n"
  },
  {
    "path": "src/includes_normalize.h",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef INCLUDES_NORMALIZE_H_\n#define INCLUDES_NORMALIZE_H_\n\n#include <string>\n#include <vector>\n\nstruct StringPiece;\n\n/// Utility functions for normalizing include paths on Windows.\n/// TODO: this likely duplicates functionality of CanonicalizePath; refactor.\nstruct IncludesNormalize {\n  /// Normalize path relative to |relative_to|.\n  IncludesNormalize(const std::string& relative_to);\n\n  // Internal utilities made available for testing, maybe useful otherwise.\n  static std::string AbsPath(StringPiece s, std::string* err);\n  static std::string Relativize(StringPiece path,\n                                const std::vector<StringPiece>& start_list,\n                                std::string* err);\n\n  /// Normalize by fixing slashes style, fixing redundant .. and . and makes the\n  /// path |input| relative to |this->relative_to_| and store to |result|.\n  bool Normalize(const std::string& input, std::string* result,\n                 std::string* err) const;\n\n private:\n  std::string relative_to_;\n  std::vector<StringPiece> split_relative_to_;\n};\n\n#endif  // INCLUDES_NORMALIZE_H_\n"
  },
  {
    "path": "src/includes_normalize_test.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"includes_normalize.h\"\n\n#include <algorithm>\n\n#include <direct.h>\n\n#include \"string_piece_util.h\"\n#include \"test.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nnamespace {\n\nstring GetCurDir() {\n  char buf[_MAX_PATH];\n  _getcwd(buf, sizeof(buf));\n  vector<StringPiece> parts = SplitStringPiece(buf, '\\\\');\n  return parts[parts.size() - 1].AsString();\n}\n\nstring NormalizeAndCheckNoError(const string& input) {\n  string result, err;\n  IncludesNormalize normalizer(\".\");\n  EXPECT_TRUE(normalizer.Normalize(input, &result, &err));\n  EXPECT_EQ(\"\", err);\n  return result;\n}\n\nstring NormalizeRelativeAndCheckNoError(const string& input,\n                                        const string& relative_to) {\n  string result, err;\n  IncludesNormalize normalizer(relative_to);\n  EXPECT_TRUE(normalizer.Normalize(input, &result, &err));\n  EXPECT_EQ(\"\", err);\n  return result;\n}\n\n}  // namespace\n\nTEST(IncludesNormalize, Simple) {\n  EXPECT_EQ(\"b\", NormalizeAndCheckNoError(\"a\\\\..\\\\b\"));\n  EXPECT_EQ(\"b\", NormalizeAndCheckNoError(\"a\\\\../b\"));\n  EXPECT_EQ(\"a/b\", NormalizeAndCheckNoError(\"a\\\\.\\\\b\"));\n  EXPECT_EQ(\"a/b\", NormalizeAndCheckNoError(\"a\\\\./b\"));\n}\n\nTEST(IncludesNormalize, WithRelative) {\n  string err;\n  string currentdir = GetCurDir();\n  EXPECT_EQ(\"c\", NormalizeRelativeAndCheckNoError(\"a/b/c\", \"a/b\"));\n  EXPECT_EQ(\"a\",\n            NormalizeAndCheckNoError(IncludesNormalize::AbsPath(\"a\", &err)));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(string(\"../\") + currentdir + string(\"/a\"),\n            NormalizeRelativeAndCheckNoError(\"a\", \"../b\"));\n  EXPECT_EQ(string(\"../\") + currentdir + string(\"/a/b\"),\n            NormalizeRelativeAndCheckNoError(\"a/b\", \"../c\"));\n  EXPECT_EQ(\"../../a\", NormalizeRelativeAndCheckNoError(\"a\", \"b/c\"));\n  EXPECT_EQ(\".\", NormalizeRelativeAndCheckNoError(\"a\", \"a\"));\n}\n\nTEST(IncludesNormalize, Case) {\n  EXPECT_EQ(\"b\", NormalizeAndCheckNoError(\"Abc\\\\..\\\\b\"));\n  EXPECT_EQ(\"BdEf\", NormalizeAndCheckNoError(\"Abc\\\\..\\\\BdEf\"));\n  EXPECT_EQ(\"A/b\", NormalizeAndCheckNoError(\"A\\\\.\\\\b\"));\n  EXPECT_EQ(\"a/b\", NormalizeAndCheckNoError(\"a\\\\./b\"));\n  EXPECT_EQ(\"A/B\", NormalizeAndCheckNoError(\"A\\\\.\\\\B\"));\n  EXPECT_EQ(\"A/B\", NormalizeAndCheckNoError(\"A\\\\./B\"));\n}\n\nTEST(IncludesNormalize, DifferentDrive) {\n  EXPECT_EQ(\"stuff.h\",\n            NormalizeRelativeAndCheckNoError(\"p:\\\\vs08\\\\stuff.h\", \"p:\\\\vs08\"));\n  EXPECT_EQ(\"stuff.h\",\n            NormalizeRelativeAndCheckNoError(\"P:\\\\Vs08\\\\stuff.h\", \"p:\\\\vs08\"));\n  EXPECT_EQ(\"p:/vs08/stuff.h\",\n            NormalizeRelativeAndCheckNoError(\"p:\\\\vs08\\\\stuff.h\", \"c:\\\\vs08\"));\n  EXPECT_EQ(\"P:/vs08/stufF.h\", NormalizeRelativeAndCheckNoError(\n                                   \"P:\\\\vs08\\\\stufF.h\", \"D:\\\\stuff/things\"));\n  EXPECT_EQ(\"P:/vs08/stuff.h\", NormalizeRelativeAndCheckNoError(\n                                   \"P:/vs08\\\\stuff.h\", \"D:\\\\stuff/things\"));\n  EXPECT_EQ(\"P:/wee/stuff.h\",\n            NormalizeRelativeAndCheckNoError(\"P:/vs08\\\\../wee\\\\stuff.h\",\n                                             \"D:\\\\stuff/things\"));\n}\n\nTEST(IncludesNormalize, LongInvalidPath) {\n  const char kLongInputString[] =\n      \"C:\\\\Program Files (x86)\\\\Microsoft Visual Studio \"\n      \"12.0\\\\VC\\\\INCLUDEwarning #31001: The dll for reading and writing the \"\n      \"pdb (for example, mspdb110.dll) could not be found on your path. This \"\n      \"is usually a configuration error. Compilation will continue using /Z7 \"\n      \"instead of /Zi, but expect a similar error when you link your program.\";\n  // Too long, won't be canonicalized. Ensure doesn't crash.\n  string result, err;\n  IncludesNormalize normalizer(\".\");\n  EXPECT_FALSE(\n      normalizer.Normalize(kLongInputString, &result, &err));\n  EXPECT_EQ(\"path too long\", err);\n\n\n  // Construct max size path having cwd prefix.\n  // kExactlyMaxPath = \"$cwd\\\\a\\\\aaaa...aaaa\\0\";\n  char kExactlyMaxPath[_MAX_PATH + 1];\n  ASSERT_STRNE(_getcwd(kExactlyMaxPath, sizeof kExactlyMaxPath), NULL);\n\n  int cwd_len = strlen(kExactlyMaxPath);\n  ASSERT_LE(cwd_len + 3 + 1, _MAX_PATH);\n  kExactlyMaxPath[cwd_len] = '\\\\';\n  kExactlyMaxPath[cwd_len + 1] = 'a';\n  kExactlyMaxPath[cwd_len + 2] = '\\\\';\n\n  kExactlyMaxPath[cwd_len + 3] = 'a';\n\n  for (int i = cwd_len + 4; i < _MAX_PATH; ++i) {\n    if (i > cwd_len + 4 && i < _MAX_PATH - 1 && i % 10 == 0)\n      kExactlyMaxPath[i] = '\\\\';\n    else\n      kExactlyMaxPath[i] = 'a';\n  }\n\n  kExactlyMaxPath[_MAX_PATH] = '\\0';\n  // This is a relatively safe cast as we can expect that _MAX_PATH will never be negative\n  EXPECT_EQ(strlen(kExactlyMaxPath), static_cast<size_t>(_MAX_PATH));\n\n  string forward_slashes(kExactlyMaxPath);\n  replace(forward_slashes.begin(), forward_slashes.end(), '\\\\', '/');\n  // Make sure a path that's exactly _MAX_PATH long is canonicalized.\n  EXPECT_EQ(forward_slashes.substr(cwd_len + 1),\n            NormalizeAndCheckNoError(kExactlyMaxPath));\n}\n\nTEST(IncludesNormalize, ShortRelativeButTooLongAbsolutePath) {\n  string result, err;\n  IncludesNormalize normalizer(\".\");\n  // A short path should work\n  EXPECT_TRUE(normalizer.Normalize(\"a\", &result, &err));\n  EXPECT_EQ(\"\", err);\n\n  // Construct max size path having cwd prefix.\n  // kExactlyMaxPath = \"aaaa\\\\aaaa...aaaa\\0\";\n  char kExactlyMaxPath[_MAX_PATH + 1];\n  for (int i = 0; i < _MAX_PATH; ++i) {\n    if (i < _MAX_PATH - 1 && i % 10 == 4)\n      kExactlyMaxPath[i] = '\\\\';\n    else\n      kExactlyMaxPath[i] = 'a';\n  }\n  kExactlyMaxPath[_MAX_PATH] = '\\0';\n  EXPECT_EQ(strlen(kExactlyMaxPath), static_cast<size_t>(_MAX_PATH));\n\n  // Make sure a path that's exactly _MAX_PATH long fails with a proper error.\n  EXPECT_FALSE(normalizer.Normalize(kExactlyMaxPath, &result, &err));\n  EXPECT_TRUE(err.find(\"GetFullPathName\") != string::npos);\n}\n"
  },
  {
    "path": "src/inline.sh",
    "content": "#!/bin/sh\n#\n# Copyright 2001 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This quick script converts a text file into an #include-able header.\n# It expects the name of the variable as its first argument, and reads\n# stdin and writes stdout.\n\nvarname=\"$1\"\n\n# 'od' and 'sed' may not be available on all platforms, and may not support the\n# flags used here. We must ensure that the script exits with a non-zero exit\n# code in those cases.\nbyte_vals=$(od -t x1 -A n -v) || exit 1\nescaped_byte_vals=$(echo \"${byte_vals}\" \\\n  | sed -e 's|^[\\t ]\\{0,\\}$||g; s|[\\t ]\\{1,\\}| |g; s| \\{1,\\}$||g; s| |\\\\x|g; s|^|\"|; s|$|\"|') \\\n  || exit 1\n\n# Only write output once we have successfully generated the required data\nprintf \"const char %s[] = \\n%s;\" \"${varname}\" \"${escaped_byte_vals}\"\n"
  },
  {
    "path": "src/jobserver-posix.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <string>\n\n#include \"jobserver.h\"\n#include \"util.h\"\n\nnamespace {\n\n// Return true if |fd| is a fifo or character device.\nbool IsJobserverDescriptor(int fd) {\n  struct stat info;\n  int ret = ::fstat(fd, &info);\n  return (ret == 0) && (((info.st_mode & S_IFMT) == S_IFIFO) ||\n                        ((info.st_mode & S_IFMT) == S_IFCHR));\n}\n\n// Implementation of Jobserver::Client for Posix systems\nclass PosixJobserverClient : public Jobserver::Client {\n public:\n  virtual ~PosixJobserverClient() {\n    if (write_fd_ >= 0)\n      ::close(write_fd_);\n    if (read_fd_ >= 0)\n      ::close(read_fd_);\n  }\n\n  Jobserver::Slot TryAcquire() override {\n    if (has_implicit_slot_) {\n      has_implicit_slot_ = false;\n      return Jobserver::Slot::CreateImplicit();\n    }\n    uint8_t slot_char = '\\0';\n    ssize_t ret;\n    do {\n      ret = ::read(read_fd_, &slot_char, 1);\n    } while (ret < 0 && errno == EINTR);\n    if (ret == 1) {\n      return Jobserver::Slot::CreateExplicit(slot_char);\n    }\n    return Jobserver::Slot();\n  }\n\n  void Release(Jobserver::Slot slot) override {\n    if (!slot.IsValid())\n      return;\n\n    if (slot.IsImplicit()) {\n      assert(!has_implicit_slot_ && \"Implicit slot cannot be released twice!\");\n      has_implicit_slot_ = true;\n      return;\n    }\n\n    uint8_t slot_char = slot.GetExplicitValue();\n    ssize_t ret;\n    do {\n      ret = ::write(write_fd_, &slot_char, 1);\n    } while (ret < 0 && errno == EINTR);\n    (void)ret;  // Nothing can be done in case of error here.\n  }\n\n  // Initialize with FIFO file path.\n  bool InitWithFifo(const std::string& fifo_path, std::string* error) {\n    if (fifo_path.empty()) {\n      *error = \"Empty fifo path\";\n      return false;\n    }\n    read_fd_ = ::open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);\n    if (read_fd_ < 0) {\n      *error =\n          std::string(\"Error opening fifo for reading: \") + strerror(errno);\n      return false;\n    }\n    if (!IsJobserverDescriptor(read_fd_)) {\n      *error = \"Not a fifo path: \" + fifo_path;\n      // Let destructor close read_fd_.\n      return false;\n    }\n    write_fd_ = ::open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);\n    if (write_fd_ < 0) {\n      *error =\n          std::string(\"Error opening fifo for writing: \") + strerror(errno);\n      // Let destructor close read_fd_\n      return false;\n    }\n    return true;\n  }\n\n private:\n  // Set to true if the implicit slot has not been acquired yet.\n  bool has_implicit_slot_ = true;\n\n  // read and write descriptors.\n  int read_fd_ = -1;\n  int write_fd_ = -1;\n};\n\n}  // namespace\n\n// static\nstd::unique_ptr<Jobserver::Client> Jobserver::Client::Create(\n    const Jobserver::Config& config, std::string* error) {\n  bool success = false;\n  auto client = std::unique_ptr<PosixJobserverClient>(new PosixJobserverClient);\n  if (config.mode == Jobserver::Config::kModePosixFifo) {\n    success = client->InitWithFifo(config.path, error);\n  } else {\n    *error = \"Unsupported jobserver mode\";\n  }\n  if (!success)\n    client.reset();\n  return client;\n}\n"
  },
  {
    "path": "src/jobserver-win32.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <assert.h>\n#include <windows.h>\n\n#include \"jobserver.h\"\n#include \"util.h\"\n\nnamespace {\n\n// Implementation of Jobserver::Client for Win32 systems.\n// At the moment, only the semaphore scheme is supported,\n// even when running under Cygwin which could support the\n// pipe version, in theory.\nclass Win32JobserverClient : public Jobserver::Client {\n public:\n  virtual ~Win32JobserverClient() {\n    // NOTE: OpenSemaphore() returns NULL on failure.\n    if (IsValid()) {\n      ::CloseHandle(handle_);\n    }\n  }\n\n  Jobserver::Slot TryAcquire() override {\n    if (IsValid()) {\n      if (has_implicit_slot_) {\n        has_implicit_slot_ = false;\n        return Jobserver::Slot::CreateImplicit();\n      }\n\n      DWORD ret = ::WaitForSingleObject(handle_, 0);\n      if (ret == WAIT_OBJECT_0) {\n        // Hard-code value 1 for the explicit slot value.\n        return Jobserver::Slot::CreateExplicit(1);\n      }\n    }\n    return Jobserver::Slot();\n  }\n\n  void Release(Jobserver::Slot slot) override {\n    if (!slot.IsValid())\n      return;\n\n    if (slot.IsImplicit()) {\n      assert(!has_implicit_slot_ && \"Implicit slot cannot be released twice!\");\n      has_implicit_slot_ = true;\n      return;\n    }\n\n    // Nothing can be done in case of error here.\n    (void)::ReleaseSemaphore(handle_, 1, NULL);\n  }\n\n  bool InitWithSemaphore(const std::string& name, std::string* error) {\n    handle_ = ::OpenSemaphoreA(SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, FALSE,\n                               name.c_str());\n    if (handle_ == NULL) {\n      *error = \"Error opening semaphore: \" + GetLastErrorString();\n      return false;\n    }\n    return true;\n  }\n\n protected:\n  bool IsValid() const {\n    // NOTE: OpenSemaphore() returns NULL on failure, not INVALID_HANDLE_VALUE.\n    return handle_ != NULL;\n  }\n\n  // Set to true if the implicit slot has not been acquired yet.\n  bool has_implicit_slot_ = true;\n\n  // Semaphore handle. NULL means not in use.\n  HANDLE handle_ = NULL;\n};\n\n}  // namespace\n\n// static\nstd::unique_ptr<Jobserver::Client> Jobserver::Client::Create(\n    const Jobserver::Config& config, std::string* error) {\n  bool success = false;\n  auto client =\n      std::unique_ptr<Win32JobserverClient>(new Win32JobserverClient());\n  if (config.mode == Jobserver::Config::kModeWin32Semaphore) {\n    success = client->InitWithSemaphore(config.path, error);\n  } else {\n    *error = \"Unsupported jobserver mode\";\n  }\n  if (!success)\n    client.reset();\n  return client;\n}\n"
  },
  {
    "path": "src/jobserver.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"jobserver.h\"\n\n#include <assert.h>\n#include <stdio.h>\n\n#include <vector>\n\n#include \"string_piece.h\"\n\nnamespace {\n\n// If |input| starts with |prefix|, return true and sets |*value| to the rest\n// of the input. Otherwise return false.\nbool GetPrefixedValue(StringPiece input, StringPiece prefix,\n                      StringPiece* value) {\n  assert(prefix.len_ > 0);\n  if (input.len_ < prefix.len_ || memcmp(prefix.str_, input.str_, prefix.len_))\n    return false;\n\n  *value = StringPiece(input.str_ + prefix.len_, input.len_ - prefix.len_);\n  return true;\n}\n\n// Try to read a comma-separated pair of file descriptors from |input|.\n// On success return true and set |config->mode| accordingly. Otherwise return\n// false if the input doesn't follow the appropriate format. Note that the\n// values are not saved since pipe mode is not supported.\nbool GetFileDescriptorPair(StringPiece input, Jobserver::Config* config) {\n  int read_fd = 1, write_fd = -1;\n  std::string pair = input.AsString();\n  if (sscanf(pair.c_str(), \"%d,%d\", &read_fd, &write_fd) != 2)\n    return false;\n\n  // From\n  // https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html Any\n  // negative descriptor means the feature is disabled.\n  if (read_fd < 0 || write_fd < 0)\n    config->mode = Jobserver::Config::kModeNone;\n  else\n    config->mode = Jobserver::Config::kModePipe;\n\n  return true;\n}\n\n}  // namespace\n\n// static\nconst int16_t Jobserver::Slot::kImplicitValue;\n\nuint8_t Jobserver::Slot::GetExplicitValue() const {\n  assert(IsExplicit());\n  return static_cast<uint8_t>(value_);\n}\n\nbool Jobserver::ParseMakeFlagsValue(const char* makeflags_env,\n                                    Jobserver::Config* config,\n                                    std::string* error) {\n  *config = Config();\n\n  if (!makeflags_env || !makeflags_env[0]) {\n    /// Return default Config instance with kModeNone if input is null or empty.\n    return true;\n  }\n\n  // Decompose input into vector of space or tab separated string pieces.\n  std::vector<StringPiece> args;\n  const char* p = makeflags_env;\n  while (*p) {\n    const char* next_space = strpbrk(p, \" \\t\");\n    if (!next_space) {\n      args.emplace_back(p);\n      break;\n    }\n\n    if (next_space > p)\n      args.emplace_back(p, next_space - p);\n\n    p = next_space + 1;\n  }\n\n  // clang-format off\n  //\n  // From:\n  // https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html\n  //\n  // \"\"\"\n  // Your tool may also examine the first word of the MAKEFLAGS variable and\n  // look for the character n. If this character is present then make was\n  // invoked with the ‘-n’ option and your tool may want to stop without\n  // performing any operations.\n  // \"\"\"\n  //\n  // Where according to\n  // https://www.gnu.org/software/make/manual/html_node/Options_002fRecursion.html\n  // MAKEFLAGS begins with all \"flag letters\" passed to make.\n  //\n  // Experimentation shows that GNU Make 4.3, at least, will set MAKEFLAGS with\n  // an initial space if no letter flag are passed to its invocation (except -j),\n  // i.e.:\n  //\n  //    make -ks --> MAKEFLAGS=\"ks\"\n  //    make -j  --> MAKEFLAGS=\" -j\"\n  //    make -ksj --> MAKEFLAGS=\"ks -j\"\n  //    make -ks -j3  --> MAKEFLAGS=\"ks -j3 --jobserver-auth=3,4\"\n  //    make -j3      --> MAKEFLAGS=\" -j3 --jobserver-auth=3,4\"\n  //\n  // However, other jobserver implementation will not, for example the one\n  // at https://github.com/rust-lang/jobserver-rs will set MAKEFLAGS to just\n  // \"--jobserver-fds=R,W --jobserver-auth=R,W\" instead, without an initial\n  // space.\n  //\n  // Another implementation is from Rust's Cargo itself which will set it to\n  // \"-j --jobserver-fds=R,W --jobserver-auth=R,W\".\n  //\n  // For the record --jobserver-fds=R,W is an old undocumented and deprecated\n  // version of --jobserver-auth=R,W that was implemented by GNU Make before 4.2\n  // was released, and some tooling may depend on it. Hence it makes sense to\n  // define both --jobserver-fds and --jobserver-auth at the same time, since\n  // the last recognized one should win in client code.\n  //\n  // The initial space will have been stripped by the loop above, but we can\n  // still support the requirement by ignoring the first arg if it begins with a\n  // dash (-).\n  //\n  // clang-format on\n  if (!args.empty() && args[0][0] != '-' &&\n      memchr(args[0].str_, 'n', args[0].len_) != nullptr) {\n    return true;\n  }\n\n  // Loop over all arguments, the last one wins, except in case of errors.\n  for (const auto& arg : args) {\n    StringPiece value;\n\n    // Handle --jobserver-auth=... here.\n    if (GetPrefixedValue(arg, \"--jobserver-auth=\", &value)) {\n      if (GetFileDescriptorPair(value, config)) {\n        continue;\n      }\n      StringPiece fifo_path;\n      if (GetPrefixedValue(value, \"fifo:\", &fifo_path)) {\n        config->mode = Jobserver::Config::kModePosixFifo;\n        config->path = fifo_path.AsString();\n      } else {\n        config->mode = Jobserver::Config::kModeWin32Semaphore;\n        config->path = value.AsString();\n      }\n      continue;\n    }\n\n    // Handle --jobserver-fds which is an old undocumented variant of\n    // --jobserver-auth that only accepts a pair of file descriptor.\n    // This was replaced by --jobserver-auth=R,W in GNU Make 4.2.\n    if (GetPrefixedValue(arg, \"--jobserver-fds=\", &value)) {\n      if (!GetFileDescriptorPair(value, config)) {\n        *error = \"Invalid file descriptor pair [\" + value.AsString() + \"]\";\n        return false;\n      }\n      config->mode = Jobserver::Config::kModePipe;\n      continue;\n    }\n\n    // Ignore this argument. This assumes that MAKEFLAGS does not\n    // use spaces to separate the option from its argument, e.g.\n    // `--jobserver-auth <something>`, which has been confirmed with\n    // Make 4.3, even if it receives such a value in its own env.\n  }\n\n  return true;\n}\n\nbool Jobserver::ParseNativeMakeFlagsValue(const char* makeflags_env,\n                                          Jobserver::Config* config,\n                                          std::string* error) {\n  if (!ParseMakeFlagsValue(makeflags_env, config, error))\n    return false;\n\n  if (config->mode == Jobserver::Config::kModePipe) {\n    *error = \"Pipe-based protocol is not supported!\";\n    return false;\n  }\n#ifdef _WIN32\n  if (config->mode == Jobserver::Config::kModePosixFifo) {\n    *error = \"FIFO mode is not supported on Windows!\";\n    return false;\n  }\n#else   // !_WIN32\n  if (config->mode == Jobserver::Config::kModeWin32Semaphore) {\n    *error = \"Semaphore mode is not supported on Posix!\";\n    return false;\n  }\n#endif  // !_WIN32\n  return true;\n}\n"
  },
  {
    "path": "src/jobserver.h",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n\n#include <memory>\n#include <string>\n#include <utility>\n\n/// Jobserver provides types related to managing a pool of \"job slots\"\n/// using the GNU Make jobserver ptocol described at:\n///\n/// https://www.gnu.org/software/make/manual/html_node/Job-Slots.html\n///\nstruct Jobserver {\n  /// A Jobserver::Slot models a single job slot that can be acquired from.\n  /// or released to a jobserver pool. This class is move-only, and can\n  /// wrap three types of values:\n  ///\n  /// - An \"invalid\" value (the default), used to indicate errors, e.g.\n  ///   that no slot could be acquired from the pool.\n  ///\n  /// - The \"implicit\" value, used to model the job slot that is implicitly\n  ///   assigned to a jobserver client by the parent process that spawned\n  ///   it.\n  ///\n  /// - The \"explicit\" values, which correspond to an actual byte read from\n  ///   the slot pool's pipe (for Posix), or a semaphore decrement operation\n  ///   (for Windows).\n  ///\n  /// Use IsValid(), IsImplicit(), HasValue() to test for categories.\n  ///\n  /// TECHNICAL NOTE: This design complies with the requirements laid out\n  /// on https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html\n  /// which requires clients to write back the exact token values they\n  /// received from a Posix pipe.\n  ///\n  /// Note that *currently* all pool implementations write the same token\n  /// values to the pipe ('+' for GNU Make, and '|' for the Rust jobserver),\n  /// and do not care about the values written back by clients.\n  ///\n  struct Slot {\n    /// Default constructor creates invalid instance.\n    Slot() = default;\n\n    /// Move operations are allowed.\n    Slot(Slot&& o) noexcept : value_(o.value_) { o.value_ = -1; }\n\n    Slot& operator=(Slot&& o) noexcept {\n      if (this != &o) {\n        this->value_ = o.value_;\n        o.value_ = -1;\n      }\n      return *this;\n    }\n\n    /// Copy operations are disallowed.\n    Slot(const Slot&) = delete;\n    Slot& operator=(const Slot&) = delete;\n\n    /// Return true if this instance is valid, i.e. that it is either\n    /// implicit or explicit job slot.\n    bool IsValid() const { return value_ >= 0; }\n\n    /// Return true if this instance represents an implicit job slot.\n    bool IsImplicit() const { return value_ == kImplicitValue; }\n\n    /// Return true if this instance represents an explicit job slot\n    bool IsExplicit() const { return IsValid() && !IsImplicit(); }\n\n    /// Return value of an explicit slot. It is a runtime error to call\n    /// this from an invalid instance.\n    uint8_t GetExplicitValue() const;\n\n    /// Create instance for explicit byte value.\n    static Slot CreateExplicit(uint8_t value) {\n      return Slot(static_cast<int16_t>(value));\n    }\n\n    /// Create instance for the implicit value.\n    static Slot CreateImplicit() { return Slot(kImplicitValue); }\n\n   private:\n    Slot(int16_t value) : value_(value) {}\n\n    static constexpr int16_t kImplicitValue = 256;\n\n    int16_t value_ = -1;\n  };\n\n  /// A Jobserver::Config models how to access or implement a GNU jobserver\n  /// implementation.\n  struct Config {\n    /// Different implementation modes for the slot pool.\n    ///\n    /// kModeNone means there is no pool.\n    ///\n    /// kModePipe means that `--jobserver-auth=R,W` is used to\n    ///    pass a pair of file descriptors to client processes. This also\n    ///    matches `--jobserver-fds=R,W` which is an old undocumented\n    ///    variant of the same scheme. This mode is not supported by\n    ///    Ninja, but recognized by the parser.\n    ///\n    /// kModePosixFifo means that `--jobserver-auth=fifo:PATH` is used to\n    ///    pass the path of a Posix FIFO to client processes. This is not\n    ///    supported on Windows. Implemented by GNU Make 4.4 and above\n    ///    when `--jobserver-style=fifo` is used.\n    ///\n    /// kModeWin32Semaphore means that `--jobserver-auth=SEMAPHORE_NAME` is\n    ///    used to pass the name of a Win32 semaphore to client processes.\n    ///    This is not supported on Posix.\n    ///\n    /// kModeDefault is the default mode to enable on the current platform.\n    ///    This is an alias for kModeWin32Semaphore on Windows ,and\n    ///    kModePosixFifo on Posix.\n    enum Mode {\n      kModeNone = 0,\n      kModePipe,\n      kModePosixFifo,\n      kModeWin32Semaphore,\n#ifdef _WIN32\n      kModeDefault = kModeWin32Semaphore,\n#else   // _WIN32\n      kModeDefault = kModePosixFifo,\n#endif  // _WIN32\n    };\n\n    /// Implementation mode for the pool.\n    Mode mode = kModeNone;\n\n    /// For kModeFifo, this is the path to the Unix FIFO to use.\n    /// For kModeSemaphore, this is the name of the Win32 semaphore to use.\n    std::string path;\n\n    /// Return true if this instance matches an active implementation mode.\n    /// This does not try to validate configuration parameters though.\n    bool HasMode() { return mode != kModeNone; }\n  };\n\n  /// Parse the value of a MAKEFLAGS environment variable. On success return\n  /// true and set |*config|. On failure, return false and set |*error| to\n  /// explain what's wrong. If |makeflags_env| is nullptr or an empty string,\n  /// this returns success and sets |config->mode| to Config::kModeNone.\n  static bool ParseMakeFlagsValue(const char* makeflags_env, Config* config,\n                                  std::string* error);\n\n  /// A variant of ParseMakeFlagsValue() that will return an error if the parsed\n  /// result is not compatible with the native system. I.e.:\n  ///\n  ///   --jobserver-auth=R,W is not supported on any system (but recognized to\n  ///       provide a relevant error message to the user).\n  ///\n  ///   --jobserver-auth=NAME onlw works on Windows.\n  ///\n  ///   --jobserver-auth=fifo:PATH only works on Posix.\n  ///\n  static bool ParseNativeMakeFlagsValue(const char* makeflags_env,\n                                        Config* config, std::string* error);\n\n  /// A Jobserver::Client instance models a client of an external GNU jobserver\n  /// pool, which can be implemented as a Unix FIFO, or a Windows named\n  /// semaphore. Usage is the following:\n  ///\n  ///  - Call Jobserver::Client::Create(), passing a Config value as argument,\n  ///    (e.g. one initialized with ParseNativeMakeFlagsValue()) to create\n  ///    a new instance.\n  ///\n  ///  - Call TryAcquire() to try to acquire a job slot from the pool.\n  ///    If the result is not an invalid slot, store it until the\n  ///    corresponding command completes, then call Release() to send it\n  ///    back to the pool.\n  ///\n  ///  - It is important that all acquired slots are released to the pool,\n  ///    even if Ninja terminates early (e.g. due to a build command failing).\n  ///\n  class Client {\n   public:\n    virtual ~Client() {}\n\n    /// Try to acquire a slot from the pool. On failure, i.e. if no slot\n    /// can be acquired, this returns an invalid Token instance.\n    ///\n    /// Note that this will always return the implicit slot value the first\n    /// time this is called, without reading anything from the pool, as\n    /// specified by the protocol. This implicit value *must* be released\n    /// just like any other one. In general, users of this class should not\n    /// care about this detail, except unit-tests.\n    virtual Slot TryAcquire() { return Slot(); }\n\n    /// Release a slot to the pool. Does nothing if slot is invalid,\n    /// or if writing to the pool fails (and if this is not the implicit slot).\n    /// If the pool is destroyed before Ninja, then only the implicit slot\n    /// can be acquired in the next calls (if it was released). This simply\n    /// enforces serialization of all commands, instead of blocking.\n    virtual void Release(Slot slot) {}\n\n    /// Create a new Client instance from a given configuration. On failure,\n    /// this returns null after setting |*error|. Note that it is an error to\n    /// call this function with |config.HasMode() == false|.\n    static std::unique_ptr<Client> Create(const Config&, std::string* error);\n\n   protected:\n    Client() = default;\n  };\n};\n"
  },
  {
    "path": "src/jobserver_test.cc",
    "content": "// Copyright 2024 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"jobserver.h\"\n\n#include \"test.h\"\n\n#ifndef _WIN32\n#include <fcntl.h>\n#include <unistd.h>\n#endif\n\nnamespace {\n\n#ifndef _WIN32\nstruct ScopedTestFd {\n  explicit ScopedTestFd(int fd) : fd_(fd) {}\n\n  ~ScopedTestFd() {\n    if (IsValid())\n      ::close(fd_);\n  }\n\n  bool IsValid() const { return fd_ >= 0; }\n\n  int fd_ = -1;\n};\n#endif  // !_WIN32\n\n}  // namespace\n\nTEST(Jobserver, SlotTest) {\n  // Default construction.\n  Jobserver::Slot slot;\n  EXPECT_FALSE(slot.IsValid());\n\n  // Construct implicit slot\n  Jobserver::Slot slot0 = Jobserver::Slot::CreateImplicit();\n  EXPECT_TRUE(slot0.IsValid());\n  EXPECT_TRUE(slot0.IsImplicit());\n  EXPECT_FALSE(slot0.IsExplicit());\n\n  // Construct explicit slots\n  auto slot1 = Jobserver::Slot::CreateExplicit(10u);\n  EXPECT_TRUE(slot1.IsValid());\n  EXPECT_FALSE(slot1.IsImplicit());\n  EXPECT_TRUE(slot1.IsExplicit());\n  EXPECT_EQ(10u, slot1.GetExplicitValue());\n\n  auto slot2 = Jobserver::Slot::CreateExplicit(42u);\n  EXPECT_TRUE(slot2.IsValid());\n  EXPECT_FALSE(slot2.IsImplicit());\n  EXPECT_TRUE(slot2.IsExplicit());\n  EXPECT_EQ(42u, slot2.GetExplicitValue());\n\n  // Move operation.\n  slot2 = std::move(slot1);\n  EXPECT_FALSE(slot1.IsValid());\n  EXPECT_TRUE(slot2.IsValid());\n  EXPECT_TRUE(slot2.IsExplicit());\n  ASSERT_EQ(10u, slot2.GetExplicitValue());\n\n  slot1 = std::move(slot0);\n  EXPECT_FALSE(slot0.IsValid());\n  EXPECT_TRUE(slot1.IsValid());\n  EXPECT_TRUE(slot1.IsImplicit());\n  EXPECT_FALSE(slot1.IsExplicit());\n}\n\nTEST(Jobserver, ParseMakeFlagsValue) {\n  Jobserver::Config config;\n  std::string error;\n\n  // Passing nullptr does not crash.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(nullptr, &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModeNone, config.mode);\n\n  // Passing an empty string does not crash.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"\", &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModeNone, config.mode);\n\n  // Passing a string that only contains whitespace does not crash.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"  \\t\", &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModeNone, config.mode);\n\n  // Passing an `n` in the first word reports no mode.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"kns --jobserver-auth=fifo:foo\",\n                                             &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModeNone, config.mode);\n\n  // Passing \"--jobserver-auth=fifo:<path>\" works.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"--jobserver-auth=fifo:foo\",\n                                             &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModePosixFifo, config.mode);\n  EXPECT_EQ(\"foo\", config.path);\n\n  // Passing an initial \" -j\" or \" -j<count>\" works.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\" -j --jobserver-auth=fifo:foo\",\n                                             &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModePosixFifo, config.mode);\n  EXPECT_EQ(\"foo\", config.path);\n\n  // Passing an initial \" -j<count>\" works.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\" -j10 --jobserver-auth=fifo:foo\",\n                                             &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModePosixFifo, config.mode);\n  EXPECT_EQ(\"foo\", config.path);\n\n  // Passing an `n` in the first word _after_ a dash works though, i.e.\n  // It is not interpreted as GNU Make dry-run flag.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\n      \"-one-flag --jobserver-auth=fifo:foo\", &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModePosixFifo, config.mode);\n\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"--jobserver-auth=semaphore_name\",\n                                             &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModeWin32Semaphore, config.mode);\n  EXPECT_EQ(\"semaphore_name\", config.path);\n\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"--jobserver-auth=10,42\", &config,\n                                             &error));\n  EXPECT_EQ(Jobserver::Config::kModePipe, config.mode);\n\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"--jobserver-auth=-1,42\", &config,\n                                             &error));\n  EXPECT_EQ(Jobserver::Config::kModeNone, config.mode);\n\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\"--jobserver-auth=10,-42\", &config,\n                                             &error));\n  EXPECT_EQ(Jobserver::Config::kModeNone, config.mode);\n\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseMakeFlagsValue(\n      \"--jobserver-auth=10,42 --jobserver-fds=12,44 \"\n      \"--jobserver-auth=fifo:/tmp/fifo\",\n      &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModePosixFifo, config.mode);\n  EXPECT_EQ(\"/tmp/fifo\", config.path);\n\n  config = {};\n  error.clear();\n  ASSERT_FALSE(\n      Jobserver::ParseMakeFlagsValue(\"--jobserver-fds=10,\", &config, &error));\n  EXPECT_EQ(\"Invalid file descriptor pair [10,]\", error);\n}\n\nTEST(Jobserver, ParseNativeMakeFlagsValue) {\n  Jobserver::Config config;\n  std::string error;\n\n  // --jobserver-auth=R,W is not supported.\n  config = {};\n  error.clear();\n  EXPECT_FALSE(Jobserver::ParseNativeMakeFlagsValue(\"--jobserver-auth=3,4\",\n                                                    &config, &error));\n  EXPECT_EQ(error, \"Pipe-based protocol is not supported!\");\n\n#ifdef _WIN32\n  // --jobserver-auth=NAME works on Windows.\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseNativeMakeFlagsValue(\n      \"--jobserver-auth=semaphore_name\", &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModeWin32Semaphore, config.mode);\n  EXPECT_EQ(\"semaphore_name\", config.path);\n\n  // --jobserver-auth=fifo:PATH does not work on Windows.\n  config = {};\n  error.clear();\n  ASSERT_FALSE(Jobserver::ParseNativeMakeFlagsValue(\"--jobserver-auth=fifo:foo\",\n                                                    &config, &error));\n  EXPECT_EQ(error, \"FIFO mode is not supported on Windows!\");\n#else   // !_WIN32\n  // --jobserver-auth=NAME does not work on Posix\n  config = {};\n  error.clear();\n  ASSERT_FALSE(Jobserver::ParseNativeMakeFlagsValue(\n      \"--jobserver-auth=semaphore_name\", &config, &error));\n  EXPECT_EQ(error, \"Semaphore mode is not supported on Posix!\");\n\n  // --jobserver-auth=fifo:PATH works on Posix\n  config = {};\n  error.clear();\n  ASSERT_TRUE(Jobserver::ParseNativeMakeFlagsValue(\"--jobserver-auth=fifo:foo\",\n                                                   &config, &error));\n  EXPECT_EQ(Jobserver::Config::kModePosixFifo, config.mode);\n  EXPECT_EQ(\"foo\", config.path);\n#endif  // !_WIN32\n}\n\nTEST(Jobserver, NullJobserver) {\n  Jobserver::Config config;\n  ASSERT_EQ(Jobserver::Config::kModeNone, config.mode);\n\n  std::string error;\n  std::unique_ptr<Jobserver::Client> client =\n      Jobserver::Client::Create(config, &error);\n  EXPECT_FALSE(client.get());\n  EXPECT_EQ(\"Unsupported jobserver mode\", error);\n}\n\n#ifdef _WIN32\n\n#include <windows.h>\n\n// Scoped HANDLE class for the semaphore.\nstruct ScopedSemaphoreHandle {\n  ScopedSemaphoreHandle(HANDLE handle) : handle_(handle) {}\n  ~ScopedSemaphoreHandle() {\n    if (handle_)\n      ::CloseHandle(handle_);\n  }\n  HANDLE get() const { return handle_; }\n\n private:\n  HANDLE handle_ = NULL;\n};\n\nTEST(Jobserver, Win32SemaphoreClient) {\n  // Create semaphore with initial token count.\n  const size_t kExplicitCount = 10;\n  const char kSemaphoreName[] = \"ninja_test_jobserver_semaphore\";\n  ScopedSemaphoreHandle handle(\n      ::CreateSemaphoreA(NULL, static_cast<DWORD>(kExplicitCount),\n                         static_cast<DWORD>(kExplicitCount), kSemaphoreName));\n  ASSERT_TRUE(handle.get()) << GetLastErrorString();\n\n  // Create new client instance.\n  Jobserver::Config config;\n  config.mode = Jobserver::Config::kModeWin32Semaphore;\n  config.path = kSemaphoreName;\n\n  std::string error;\n  std::unique_ptr<Jobserver::Client> client =\n      Jobserver::Client::Create(config, &error);\n  EXPECT_TRUE(client.get()) << error;\n  EXPECT_TRUE(error.empty()) << error;\n\n  Jobserver::Slot slot;\n  std::vector<Jobserver::Slot> slots;\n\n  // Read the implicit slot.\n  slot = client->TryAcquire();\n  EXPECT_TRUE(slot.IsValid());\n  EXPECT_TRUE(slot.IsImplicit());\n  slots.push_back(std::move(slot));\n\n  // Read the explicit slots.\n  for (size_t n = 0; n < kExplicitCount; ++n) {\n    slot = client->TryAcquire();\n    EXPECT_TRUE(slot.IsValid());\n    EXPECT_TRUE(slot.IsExplicit());\n    slots.push_back(std::move(slot));\n  }\n\n  // Pool should be empty now.\n  slot = client->TryAcquire();\n  EXPECT_FALSE(slot.IsValid());\n\n  // Release the slots again.\n  while (!slots.empty()) {\n    client->Release(std::move(slots.back()));\n    slots.pop_back();\n  }\n\n  slot = client->TryAcquire();\n  EXPECT_TRUE(slot.IsValid());\n  EXPECT_TRUE(slot.IsImplicit());\n  slots.push_back(std::move(slot));\n\n  for (size_t n = 0; n < kExplicitCount; ++n) {\n    slot = client->TryAcquire();\n    EXPECT_TRUE(slot.IsValid());\n    EXPECT_TRUE(slot.IsExplicit()) << n;\n    slots.push_back(std::move(slot));\n  }\n\n  // And the pool should be empty again.\n  slot = client->TryAcquire();\n  EXPECT_FALSE(slot.IsValid());\n}\n#else   // !_WIN32\nTEST(Jobserver, PosixFifoClient) {\n  ScopedTempDir temp_dir;\n  temp_dir.CreateAndEnter(\"ninja_test_jobserver_fifo\");\n\n  // Create the Fifo, then write kSlotCount slots into it.\n  std::string fifo_path = temp_dir.temp_dir_name_ + \"fifo\";\n  int ret = mknod(fifo_path.c_str(), S_IFIFO | 0666, 0);\n  ASSERT_EQ(0, ret) << \"Could not create FIFO at: \" << fifo_path;\n\n  const size_t kSlotCount = 5;\n\n  ScopedTestFd write_fd(::open(fifo_path.c_str(), O_RDWR));\n  ASSERT_TRUE(write_fd.IsValid()) << \"Cannot open FIFO at: \" << strerror(errno);\n  for (size_t n = 0; n < kSlotCount; ++n) {\n    uint8_t slot_byte = static_cast<uint8_t>('0' + n);\n    ssize_t ret = ::write(write_fd.fd_, &slot_byte, 1);\n    (void)ret;  // make compiler happy\n  }\n  // Keep the file descriptor opened to ensure the fifo's content\n  // persists in kernel memory.\n\n  // Create new client instance.\n  Jobserver::Config config;\n  config.mode = Jobserver::Config::kModePosixFifo;\n  config.path = fifo_path;\n\n  std::string error;\n  std::unique_ptr<Jobserver::Client> client =\n      Jobserver::Client::Create(config, &error);\n  EXPECT_TRUE(client.get());\n  EXPECT_TRUE(error.empty()) << error;\n\n  // Read slots from the pool, and store them\n  std::vector<Jobserver::Slot> slots;\n\n  // First slot is always implicit.\n  slots.push_back(client->TryAcquire());\n  ASSERT_TRUE(slots.back().IsValid());\n  EXPECT_TRUE(slots.back().IsImplicit());\n\n  // Then read kSlotCount slots from the pipe and verify their value.\n  for (size_t n = 0; n < kSlotCount; ++n) {\n    Jobserver::Slot slot = client->TryAcquire();\n    ASSERT_TRUE(slot.IsValid()) << \"Slot #\" << n + 1;\n    EXPECT_EQ(static_cast<uint8_t>('0' + n), slot.GetExplicitValue());\n    slots.push_back(std::move(slot));\n  }\n\n  // Pool should be empty now, so next TryAcquire() will fail.\n  Jobserver::Slot slot = client->TryAcquire();\n  EXPECT_FALSE(slot.IsValid());\n}\n\nTEST(Jobserver, PosixFifoClientWithWrongPath) {\n  ScopedTempDir temp_dir;\n  temp_dir.CreateAndEnter(\"ninja_test_jobserver_fifo\");\n\n  // Create a regular file.\n  std::string file_path = temp_dir.temp_dir_name_ + \"not_a_fifo\";\n  int fd = ::open(file_path.c_str(), O_CREAT | O_RDWR, 0660);\n  ASSERT_GE(fd, 0) << \"Could not create file: \" << strerror(errno);\n  ::close(fd);\n\n  // Create new client instance, passing the file path for the fifo.\n  Jobserver::Config config;\n  config.mode = Jobserver::Config::kModePosixFifo;\n  config.path = file_path;\n\n  std::string error;\n  std::unique_ptr<Jobserver::Client> client =\n      Jobserver::Client::Create(config, &error);\n  EXPECT_FALSE(client.get());\n  EXPECT_FALSE(error.empty());\n  EXPECT_EQ(\"Not a fifo path: \" + file_path, error);\n\n  // Do the same with an empty file path.\n  error.clear();\n  config.path.clear();\n  client = Jobserver::Client::Create(config, &error);\n  EXPECT_FALSE(client.get());\n  EXPECT_FALSE(error.empty());\n  EXPECT_EQ(\"Empty fifo path\", error);\n}\n#endif  // !_WIN32\n"
  },
  {
    "path": "src/json.cc",
    "content": "// Copyright 2021 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"json.h\"\n\n#include <cstdio>\n#include <string>\n\nstd::string EncodeJSONString(const std::string& in) {\n  static const char* hex_digits = \"0123456789abcdef\";\n  std::string out;\n  out.reserve(in.length() * 1.2);\n  for (std::string::const_iterator it = in.begin(); it != in.end(); ++it) {\n    char c = *it;\n    if (c == '\\b')\n      out += \"\\\\b\";\n    else if (c == '\\f')\n      out += \"\\\\f\";\n    else if (c == '\\n')\n      out += \"\\\\n\";\n    else if (c == '\\r')\n      out += \"\\\\r\";\n    else if (c == '\\t')\n      out += \"\\\\t\";\n    else if (0x0 <= c && c < 0x20) {\n      out += \"\\\\u00\";\n      out += hex_digits[c >> 4];\n      out += hex_digits[c & 0xf];\n    } else if (c == '\\\\')\n      out += \"\\\\\\\\\";\n    else if (c == '\\\"')\n      out += \"\\\\\\\"\";\n    else\n      out += c;\n  }\n  return out;\n}\n\nvoid PrintJSONString(const std::string& in) {\n  std::string out = EncodeJSONString(in);\n  fwrite(out.c_str(), 1, out.length(), stdout);\n}\n"
  },
  {
    "path": "src/json.h",
    "content": "// Copyright 2021 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_JSON_H_\n#define NINJA_JSON_H_\n\n#include <string>\n\n// Encode a string in JSON format without enclosing quotes\nstd::string EncodeJSONString(const std::string& in);\n\n// Print a string in JSON format to stdout without enclosing quotes\nvoid PrintJSONString(const std::string& in);\n\n#endif\n"
  },
  {
    "path": "src/json_test.cc",
    "content": "// Copyright 2021 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"json.h\"\n\n#include \"test.h\"\n\nTEST(JSONTest, RegularAscii) {\n  EXPECT_EQ(EncodeJSONString(\"foo bar\"), \"foo bar\");\n}\n\nTEST(JSONTest, EscapedChars) {\n  EXPECT_EQ(EncodeJSONString(\"\\\"\\\\\\b\\f\\n\\r\\t\"),\n            \"\\\\\\\"\"\n            \"\\\\\\\\\"\n            \"\\\\b\\\\f\\\\n\\\\r\\\\t\");\n}\n\n// codepoints between 0 and 0x1f should be escaped\nTEST(JSONTest, ControlChars) {\n  EXPECT_EQ(EncodeJSONString(\"\\x01\\x1f\"), \"\\\\u0001\\\\u001f\");\n}\n\n// Leave them alone as JSON accepts unicode literals\n// out of control character range\nTEST(JSONTest, UTF8) {\n  const char* utf8str = \"\\xe4\\xbd\\xa0\\xe5\\xa5\\xbd\";\n  EXPECT_EQ(EncodeJSONString(utf8str), utf8str);\n}\n"
  },
  {
    "path": "src/lexer.cc",
    "content": "/* Generated by re2c */\n// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"lexer.h\"\n\n#include <stdio.h>\n\n#include \"eval_env.h\"\n#include \"util.h\"\n\nusing namespace std;\n\n// $^ supported starting this version\nconst int kMinNewlineEscapeVersionMajor = 1;\nconst int kMinNewlineEscapeVersionMinor = 14;\n\nbool Lexer::Error(const string& message, string* err) {\n  // Compute line/column.\n  int line = 1;\n  const char* line_start = input_.str_;\n  for (const char* p = input_.str_; p < last_token_; ++p) {\n    if (*p == '\\n') {\n      ++line;\n      line_start = p + 1;\n    }\n  }\n  int col = last_token_ ? (int)(last_token_ - line_start) : 0;\n\n  char buf[1024];\n  snprintf(buf, sizeof(buf), \"%s:%d: \", filename_.AsString().c_str(), line);\n  *err = buf;\n  *err += message + \"\\n\";\n\n  // Add some context to the message.\n  const int kTruncateColumn = 72;\n  if (col > 0 && col < kTruncateColumn) {\n    int len;\n    bool truncated = true;\n    for (len = 0; len < kTruncateColumn; ++len) {\n      if (line_start[len] == 0 || line_start[len] == '\\n') {\n        truncated = false;\n        break;\n      }\n    }\n    *err += string(line_start, len);\n    if (truncated)\n      *err += \"...\";\n    *err += \"\\n\";\n    *err += string(col, ' ');\n    *err += \"^ near here\";\n  }\n\n  return false;\n}\n\nLexer::Lexer(const char* input) {\n  Start(\"input\", input);\n}\n\nvoid Lexer::Start(StringPiece filename, StringPiece input) {\n  filename_ = filename;\n  input_ = input;\n  ofs_ = input_.str_;\n  last_token_ = NULL;\n}\n\nconst char* Lexer::TokenName(Token t) {\n  switch (t) {\n  case ERROR:    return \"lexing error\";\n  case BUILD:    return \"'build'\";\n  case COLON:    return \"':'\";\n  case DEFAULT:  return \"'default'\";\n  case EQUALS:   return \"'='\";\n  case IDENT:    return \"identifier\";\n  case INCLUDE:  return \"'include'\";\n  case INDENT:   return \"indent\";\n  case NEWLINE:  return \"newline\";\n  case PIPE2:    return \"'||'\";\n  case PIPE:     return \"'|'\";\n  case PIPEAT:   return \"'|@'\";\n  case POOL:     return \"'pool'\";\n  case RULE:     return \"'rule'\";\n  case SUBNINJA: return \"'subninja'\";\n  case TEOF:     return \"eof\";\n  }\n  return NULL;  // not reached\n}\n\nconst char* Lexer::TokenErrorHint(Token expected) {\n  switch (expected) {\n  case COLON:\n    return \" ($ also escapes ':')\";\n  default:\n    return \"\";\n  }\n}\n\nstring Lexer::DescribeLastError() {\n  if (last_token_) {\n    switch (last_token_[0]) {\n    case '\\t':\n      return \"tabs are not allowed, use spaces\";\n    }\n  }\n  return \"lexing error\";\n}\n\nvoid Lexer::UnreadToken() {\n  ofs_ = last_token_;\n}\n\nLexer::Token Lexer::ReadToken() {\n  const char* p = ofs_;\n  const char* q;\n  const char* start;\n  Lexer::Token token;\n  for (;;) {\n    start = p;\n    \n{\n\tunsigned char yych;\n\tunsigned int yyaccept = 0;\n\tstatic const unsigned char yybm[256] = {\n\t\t  0, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128,   0, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t160, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 192, 192, 128,\n\t\t192, 192, 192, 192, 192, 192, 192, 192,\n\t\t192, 192, 128, 128, 128, 128, 128, 128,\n\t\t128, 192, 192, 192, 192, 192, 192, 192,\n\t\t192, 192, 192, 192, 192, 192, 192, 192,\n\t\t192, 192, 192, 192, 192, 192, 192, 192,\n\t\t192, 192, 192, 128, 128, 128, 128, 192,\n\t\t128, 192, 192, 192, 192, 192, 192, 192,\n\t\t192, 192, 192, 192, 192, 192, 192, 192,\n\t\t192, 192, 192, 192, 192, 192, 192, 192,\n\t\t192, 192, 192, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128\n\t};\n\tyych = *p;\n\tif (yybm[0+yych] & 32) goto yy6;\n\tif (yych <= '^') {\n\t\tif (yych <= ',') {\n\t\t\tif (yych <= '\\f') {\n\t\t\t\tif (yych <= 0x00) goto yy1;\n\t\t\t\tif (yych == '\\n') goto yy4;\n\t\t\t\tgoto yy2;\n\t\t\t} else {\n\t\t\t\tif (yych <= '\\r') goto yy5;\n\t\t\t\tif (yych == '#') goto yy8;\n\t\t\t\tgoto yy2;\n\t\t\t}\n\t\t} else {\n\t\t\tif (yych <= ':') {\n\t\t\t\tif (yych == '/') goto yy2;\n\t\t\t\tif (yych <= '9') goto yy9;\n\t\t\t\tgoto yy11;\n\t\t\t} else {\n\t\t\t\tif (yych <= '=') {\n\t\t\t\t\tif (yych <= '<') goto yy2;\n\t\t\t\t\tgoto yy12;\n\t\t\t\t} else {\n\t\t\t\t\tif (yych <= '@') goto yy2;\n\t\t\t\t\tif (yych <= 'Z') goto yy9;\n\t\t\t\t\tgoto yy2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (yych <= 'i') {\n\t\t\tif (yych <= 'b') {\n\t\t\t\tif (yych == '`') goto yy2;\n\t\t\t\tif (yych <= 'a') goto yy9;\n\t\t\t\tgoto yy13;\n\t\t\t} else {\n\t\t\t\tif (yych == 'd') goto yy14;\n\t\t\t\tif (yych <= 'h') goto yy9;\n\t\t\t\tgoto yy15;\n\t\t\t}\n\t\t} else {\n\t\t\tif (yych <= 'r') {\n\t\t\t\tif (yych == 'p') goto yy16;\n\t\t\t\tif (yych <= 'q') goto yy9;\n\t\t\t\tgoto yy17;\n\t\t\t} else {\n\t\t\t\tif (yych <= 'z') {\n\t\t\t\t\tif (yych <= 's') goto yy18;\n\t\t\t\t\tgoto yy9;\n\t\t\t\t} else {\n\t\t\t\t\tif (yych == '|') goto yy19;\n\t\t\t\t\tgoto yy2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\nyy1:\n\t++p;\n\t{ token = TEOF;     break; }\nyy2:\n\t++p;\nyy3:\n\t{ token = ERROR;    break; }\nyy4:\n\t++p;\n\t{ token = NEWLINE;  break; }\nyy5:\n\tyych = *++p;\n\tif (yych == '\\n') goto yy20;\n\tgoto yy3;\nyy6:\n\tyyaccept = 0;\n\tyych = *(q = ++p);\n\tif (yybm[0+yych] & 32) goto yy6;\n\tif (yych <= '\\f') {\n\t\tif (yych == '\\n') goto yy4;\n\t} else {\n\t\tif (yych <= '\\r') goto yy21;\n\t\tif (yych == '#') goto yy23;\n\t}\nyy7:\n\t{ token = INDENT;   break; }\nyy8:\n\tyyaccept = 1;\n\tyych = *(q = ++p);\n\tif (yych <= 0x00) goto yy3;\n\tgoto yy24;\nyy9:\n\tyych = *++p;\nyy10:\n\tif (yybm[0+yych] & 64) goto yy9;\n\t{ token = IDENT;    break; }\nyy11:\n\t++p;\n\t{ token = COLON;    break; }\nyy12:\n\t++p;\n\t{ token = EQUALS;   break; }\nyy13:\n\tyych = *++p;\n\tif (yych == 'u') goto yy25;\n\tgoto yy10;\nyy14:\n\tyych = *++p;\n\tif (yych == 'e') goto yy26;\n\tgoto yy10;\nyy15:\n\tyych = *++p;\n\tif (yych == 'n') goto yy27;\n\tgoto yy10;\nyy16:\n\tyych = *++p;\n\tif (yych == 'o') goto yy28;\n\tgoto yy10;\nyy17:\n\tyych = *++p;\n\tif (yych == 'u') goto yy29;\n\tgoto yy10;\nyy18:\n\tyych = *++p;\n\tif (yych == 'u') goto yy30;\n\tgoto yy10;\nyy19:\n\tyych = *++p;\n\tif (yych == '@') goto yy31;\n\tif (yych == '|') goto yy32;\n\t{ token = PIPE;     break; }\nyy20:\n\t++p;\n\t{ token = NEWLINE;  break; }\nyy21:\n\tyych = *++p;\n\tif (yych == '\\n') goto yy20;\nyy22:\n\tp = q;\n\tif (yyaccept == 0) goto yy7;\n\telse goto yy3;\nyy23:\n\tyych = *++p;\nyy24:\n\tif (yybm[0+yych] & 128) goto yy23;\n\tif (yych <= 0x00) goto yy22;\n\t++p;\n\t{ continue; }\nyy25:\n\tyych = *++p;\n\tif (yych == 'i') goto yy33;\n\tgoto yy10;\nyy26:\n\tyych = *++p;\n\tif (yych == 'f') goto yy34;\n\tgoto yy10;\nyy27:\n\tyych = *++p;\n\tif (yych == 'c') goto yy35;\n\tgoto yy10;\nyy28:\n\tyych = *++p;\n\tif (yych == 'o') goto yy36;\n\tgoto yy10;\nyy29:\n\tyych = *++p;\n\tif (yych == 'l') goto yy37;\n\tgoto yy10;\nyy30:\n\tyych = *++p;\n\tif (yych == 'b') goto yy38;\n\tgoto yy10;\nyy31:\n\t++p;\n\t{ token = PIPEAT;   break; }\nyy32:\n\t++p;\n\t{ token = PIPE2;    break; }\nyy33:\n\tyych = *++p;\n\tif (yych == 'l') goto yy39;\n\tgoto yy10;\nyy34:\n\tyych = *++p;\n\tif (yych == 'a') goto yy40;\n\tgoto yy10;\nyy35:\n\tyych = *++p;\n\tif (yych == 'l') goto yy41;\n\tgoto yy10;\nyy36:\n\tyych = *++p;\n\tif (yych == 'l') goto yy42;\n\tgoto yy10;\nyy37:\n\tyych = *++p;\n\tif (yych == 'e') goto yy43;\n\tgoto yy10;\nyy38:\n\tyych = *++p;\n\tif (yych == 'n') goto yy44;\n\tgoto yy10;\nyy39:\n\tyych = *++p;\n\tif (yych == 'd') goto yy45;\n\tgoto yy10;\nyy40:\n\tyych = *++p;\n\tif (yych == 'u') goto yy46;\n\tgoto yy10;\nyy41:\n\tyych = *++p;\n\tif (yych == 'u') goto yy47;\n\tgoto yy10;\nyy42:\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy9;\n\t{ token = POOL;     break; }\nyy43:\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy9;\n\t{ token = RULE;     break; }\nyy44:\n\tyych = *++p;\n\tif (yych == 'i') goto yy48;\n\tgoto yy10;\nyy45:\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy9;\n\t{ token = BUILD;    break; }\nyy46:\n\tyych = *++p;\n\tif (yych == 'l') goto yy49;\n\tgoto yy10;\nyy47:\n\tyych = *++p;\n\tif (yych == 'd') goto yy50;\n\tgoto yy10;\nyy48:\n\tyych = *++p;\n\tif (yych == 'n') goto yy51;\n\tgoto yy10;\nyy49:\n\tyych = *++p;\n\tif (yych == 't') goto yy52;\n\tgoto yy10;\nyy50:\n\tyych = *++p;\n\tif (yych == 'e') goto yy53;\n\tgoto yy10;\nyy51:\n\tyych = *++p;\n\tif (yych == 'j') goto yy54;\n\tgoto yy10;\nyy52:\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy9;\n\t{ token = DEFAULT;  break; }\nyy53:\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy9;\n\t{ token = INCLUDE;  break; }\nyy54:\n\tyych = *++p;\n\tif (yych != 'a') goto yy10;\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy9;\n\t{ token = SUBNINJA; break; }\n}\n\n  }\n\n  last_token_ = start;\n  ofs_ = p;\n  if (token != NEWLINE && token != TEOF)\n    EatWhitespace();\n  return token;\n}\n\nbool Lexer::PeekToken(Token token) {\n  Token t = ReadToken();\n  if (t == token)\n    return true;\n  UnreadToken();\n  return false;\n}\n\nvoid Lexer::EatWhitespace() {\n  const char* p = ofs_;\n  const char* q;\n  for (;;) {\n    ofs_ = p;\n    \n{\n\tunsigned char yych;\n\tstatic const unsigned char yybm[256] = {\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t128,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0\n\t};\n\tyych = *p;\n\tif (yybm[0+yych] & 128) goto yy59;\n\tif (yych <= 0x00) goto yy56;\n\tif (yych == '$') goto yy60;\n\tgoto yy57;\nyy56:\n\t++p;\n\t{ break; }\nyy57:\n\t++p;\nyy58:\n\t{ break; }\nyy59:\n\tyych = *++p;\n\tif (yybm[0+yych] & 128) goto yy59;\n\t{ continue; }\nyy60:\n\tyych = *(q = ++p);\n\tif (yych == '\\n') goto yy61;\n\tif (yych == '\\r') goto yy62;\n\tgoto yy58;\nyy61:\n\t++p;\n\t{ continue; }\nyy62:\n\tyych = *++p;\n\tif (yych == '\\n') goto yy63;\n\tp = q;\n\tgoto yy58;\nyy63:\n\t++p;\n\t{ continue; }\n}\n\n  }\n}\n\nbool Lexer::ReadIdent(string* out) {\n  const char* p = ofs_;\n  const char* start;\n  for (;;) {\n    start = p;\n    \n{\n\tunsigned char yych;\n\tstatic const unsigned char yybm[256] = {\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0, 128, 128,   0,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128,   0,   0,   0,   0,   0,   0,\n\t\t  0, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128,   0,   0,   0,   0, 128,\n\t\t  0, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128, 128, 128, 128, 128, 128,\n\t\t128, 128, 128,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0,\n\t\t  0,   0,   0,   0,   0,   0,   0,   0\n\t};\n\tyych = *p;\n\tif (yybm[0+yych] & 128) goto yy65;\n\t++p;\n\t{\n      last_token_ = start;\n      return false;\n    }\nyy65:\n\tyych = *++p;\n\tif (yybm[0+yych] & 128) goto yy65;\n\t{\n      out->assign(start, p - start);\n      break;\n    }\n}\n\n  }\n  last_token_ = start;\n  ofs_ = p;\n  EatWhitespace();\n  return true;\n}\n\nbool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) {\n  const char* p = ofs_;\n  const char* q;\n  const char* start;\n  for (;;) {\n    start = p;\n    \n{\n\tunsigned char yych;\n\tstatic const unsigned char yybm[256] = {\n\t\t  0,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,   0,  16,  16,   0,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 32,  16,  16,  16,   0,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16, 208, 144,  16,\n\t\t208, 208, 208, 208, 208, 208, 208, 208,\n\t\t208, 208,   0,  16,  16,  16,  16,  16,\n\t\t 16, 208, 208, 208, 208, 208, 208, 208,\n\t\t208, 208, 208, 208, 208, 208, 208, 208,\n\t\t208, 208, 208, 208, 208, 208, 208, 208,\n\t\t208, 208, 208,  16,  16,  16,  16, 208,\n\t\t 16, 208, 208, 208, 208, 208, 208, 208,\n\t\t208, 208, 208, 208, 208, 208, 208, 208,\n\t\t208, 208, 208, 208, 208, 208, 208, 208,\n\t\t208, 208, 208,  16,   0,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16,\n\t\t 16,  16,  16,  16,  16,  16,  16,  16\n\t};\n\tyych = *p;\n\tif (yybm[0+yych] & 16) goto yy68;\n\tif (yych <= '\\r') {\n\t\tif (yych <= 0x00) goto yy67;\n\t\tif (yych <= '\\n') goto yy69;\n\t\tgoto yy70;\n\t} else {\n\t\tif (yych <= ' ') goto yy69;\n\t\tif (yych <= '$') goto yy71;\n\t\tgoto yy69;\n\t}\nyy67:\n\t++p;\n\t{\n      last_token_ = start;\n      return Error(\"unexpected EOF\", err);\n    }\nyy68:\n\tyych = *++p;\n\tif (yybm[0+yych] & 16) goto yy68;\n\t{\n      eval->AddText(StringPiece(start, p - start));\n      continue;\n    }\nyy69:\n\t++p;\n\t{\n      if (path) {\n        p = start;\n        break;\n      } else {\n        if (*start == '\\n')\n          break;\n        eval->AddText(StringPiece(start, 1));\n        continue;\n      }\n    }\nyy70:\n\tyych = *++p;\n\tif (yych == '\\n') goto yy72;\n\t{\n      last_token_ = start;\n      return Error(DescribeLastError(), err);\n    }\nyy71:\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy79;\n\tif (yych <= '#') {\n\t\tif (yych <= '\\f') {\n\t\t\tif (yych == '\\n') goto yy75;\n\t\t\tgoto yy73;\n\t\t} else {\n\t\t\tif (yych <= '\\r') goto yy76;\n\t\t\tif (yych == ' ') goto yy77;\n\t\t\tgoto yy73;\n\t\t}\n\t} else {\n\t\tif (yych <= ']') {\n\t\t\tif (yych <= '$') goto yy78;\n\t\t\tif (yych <= '/') goto yy73;\n\t\t\tif (yych <= ':') goto yy80;\n\t\t\tgoto yy73;\n\t\t} else {\n\t\t\tif (yych <= '^') goto yy81;\n\t\t\tif (yych <= '`') goto yy73;\n\t\t\tif (yych <= '{') goto yy82;\n\t\t\tgoto yy73;\n\t\t}\n\t}\nyy72:\n\t++p;\n\t{\n      if (path)\n        p = start;\n      break;\n    }\nyy73:\n\t++p;\nyy74:\n\t{\n      last_token_ = start;\n      return Error(\"bad $-escape (literal $ must be written as $$)\", err);\n    }\nyy75:\n\tyych = *++p;\n\tif (yybm[0+yych] & 32) goto yy75;\n\t{\n      continue;\n    }\nyy76:\n\tyych = *++p;\n\tif (yych == '\\n') goto yy83;\n\tgoto yy74;\nyy77:\n\t++p;\n\t{\n      eval->AddText(StringPiece(\" \", 1));\n      continue;\n    }\nyy78:\n\t++p;\n\t{\n      eval->AddText(StringPiece(\"$\", 1));\n      continue;\n    }\nyy79:\n\tyych = *++p;\n\tif (yybm[0+yych] & 64) goto yy79;\n\t{\n      eval->AddSpecial(StringPiece(start + 1, p - start - 1));\n      continue;\n    }\nyy80:\n\t++p;\n\t{\n      eval->AddText(StringPiece(\":\", 1));\n      continue;\n    }\nyy81:\n\t++p;\n\t{\n      if (!newline_version_checked_)\n      {\n        if ((manifest_version_major < kMinNewlineEscapeVersionMajor) ||\n            (manifest_version_major = kMinNewlineEscapeVersionMajor &&\n             manifest_version_minor < kMinNewlineEscapeVersionMinor))\n        {\n          return Error(\"using $^ escape requires specifying 'ninja_required_version' with version greater or equal 1.14\", err);\n        }\n        newline_version_checked_ = true;\n      }\n      eval->AddText(StringPiece(\"\\n\", 1));\n      continue;\n    }\nyy82:\n\tyych = *(q = ++p);\n\tif (yybm[0+yych] & 128) goto yy84;\n\tgoto yy74;\nyy83:\n\tyych = *++p;\n\tif (yych == ' ') goto yy83;\n\t{\n      continue;\n    }\nyy84:\n\tyych = *++p;\n\tif (yybm[0+yych] & 128) goto yy84;\n\tif (yych == '}') goto yy85;\n\tp = q;\n\tgoto yy74;\nyy85:\n\t++p;\n\t{\n      eval->AddSpecial(StringPiece(start + 2, p - start - 3));\n      continue;\n    }\n}\n\n  }\n  last_token_ = start;\n  ofs_ = p;\n  if (path)\n    EatWhitespace();\n  // Non-path strings end in newlines, so there's no whitespace to eat.\n  return true;\n}\n"
  },
  {
    "path": "src/lexer.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_LEXER_H_\n#define NINJA_LEXER_H_\n\n#include \"string_piece.h\"\n\n// Windows may #define ERROR.\n#ifdef ERROR\n#undef ERROR\n#endif\n\nstruct EvalString;\n\nstruct Lexer {\n  Lexer() {}\n  /// Helper ctor useful for tests.\n  explicit Lexer(const char* input);\n\n  enum Token {\n    ERROR,\n    BUILD,\n    COLON,\n    DEFAULT,\n    EQUALS,\n    IDENT,\n    INCLUDE,\n    INDENT,\n    NEWLINE,\n    PIPE,\n    PIPE2,\n    PIPEAT,\n    POOL,\n    RULE,\n    SUBNINJA,\n    TEOF,\n  };\n\n  /// Return a human-readable form of a token, used in error messages.\n  static const char* TokenName(Token t);\n\n  /// Return a human-readable token hint, used in error messages.\n  static const char* TokenErrorHint(Token expected);\n\n  /// If the last token read was an ERROR token, provide more info\n  /// or the empty string.\n  std::string DescribeLastError();\n\n  /// Start parsing some input.\n  void Start(StringPiece filename, StringPiece input);\n\n  /// Read a Token from the Token enum.\n  Token ReadToken();\n\n  /// Rewind to the last read Token.\n  void UnreadToken();\n\n  /// If the next token is \\a token, read it and return true.\n  bool PeekToken(Token token);\n\n  /// Read a simple identifier (a rule or variable name).\n  /// Returns false if a name can't be read.\n  bool ReadIdent(std::string* out);\n\n  /// Read a path (complete with $escapes).\n  /// Returns false only on error, returned path may be empty if a delimiter\n  /// (space, newline) is hit.\n  bool ReadPath(EvalString* path, std::string* err) {\n    return ReadEvalString(path, true, err);\n  }\n\n  /// Read the value side of a var = value line (complete with $escapes).\n  /// Returns false only on error.\n  bool ReadVarValue(EvalString* value, std::string* err) {\n    return ReadEvalString(value, false, err);\n  }\n\n  /// Construct an error message with context.\n  bool Error(const std::string& message, std::string* err);\n\n  /// Parsed 'ninja_required_version' from the manifest.\n  int manifest_version_major = 0;\n  int manifest_version_minor = 0;\n\nprivate:\n  /// Skip past whitespace (called after each read token/ident/etc.).\n  void EatWhitespace();\n\n  /// Read a $-escaped string.\n  bool ReadEvalString(EvalString* eval, bool path, std::string* err);\n\n  StringPiece filename_;\n  StringPiece input_;\n  const char* ofs_;\n  const char* last_token_;\n\n  /// Holds true if ninja_required_version checked for $^ (newline) escape.\n  bool newline_version_checked_ = false;\n};\n\n#endif // NINJA_LEXER_H_\n"
  },
  {
    "path": "src/lexer.in.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"lexer.h\"\n\n#include <stdio.h>\n\n#include \"eval_env.h\"\n#include \"util.h\"\n\nusing namespace std;\n\n// $^ supported starting this version\nconst int kMinNewlineEscapeVersionMajor = 1;\nconst int kMinNewlineEscapeVersionMinor = 14;\n\nbool Lexer::Error(const string& message, string* err) {\n  // Compute line/column.\n  int line = 1;\n  const char* line_start = input_.str_;\n  for (const char* p = input_.str_; p < last_token_; ++p) {\n    if (*p == '\\n') {\n      ++line;\n      line_start = p + 1;\n    }\n  }\n  int col = last_token_ ? (int)(last_token_ - line_start) : 0;\n\n  char buf[1024];\n  snprintf(buf, sizeof(buf), \"%s:%d: \", filename_.AsString().c_str(), line);\n  *err = buf;\n  *err += message + \"\\n\";\n\n  // Add some context to the message.\n  const int kTruncateColumn = 72;\n  if (col > 0 && col < kTruncateColumn) {\n    int len;\n    bool truncated = true;\n    for (len = 0; len < kTruncateColumn; ++len) {\n      if (line_start[len] == 0 || line_start[len] == '\\n') {\n        truncated = false;\n        break;\n      }\n    }\n    *err += string(line_start, len);\n    if (truncated)\n      *err += \"...\";\n    *err += \"\\n\";\n    *err += string(col, ' ');\n    *err += \"^ near here\";\n  }\n\n  return false;\n}\n\nLexer::Lexer(const char* input) {\n  Start(\"input\", input);\n}\n\nvoid Lexer::Start(StringPiece filename, StringPiece input) {\n  filename_ = filename;\n  input_ = input;\n  ofs_ = input_.str_;\n  last_token_ = NULL;\n}\n\nconst char* Lexer::TokenName(Token t) {\n  switch (t) {\n  case ERROR:    return \"lexing error\";\n  case BUILD:    return \"'build'\";\n  case COLON:    return \"':'\";\n  case DEFAULT:  return \"'default'\";\n  case EQUALS:   return \"'='\";\n  case IDENT:    return \"identifier\";\n  case INCLUDE:  return \"'include'\";\n  case INDENT:   return \"indent\";\n  case NEWLINE:  return \"newline\";\n  case PIPE2:    return \"'||'\";\n  case PIPE:     return \"'|'\";\n  case PIPEAT:   return \"'|@'\";\n  case POOL:     return \"'pool'\";\n  case RULE:     return \"'rule'\";\n  case SUBNINJA: return \"'subninja'\";\n  case TEOF:     return \"eof\";\n  }\n  return NULL;  // not reached\n}\n\nconst char* Lexer::TokenErrorHint(Token expected) {\n  switch (expected) {\n  case COLON:\n    return \" ($ also escapes ':')\";\n  default:\n    return \"\";\n  }\n}\n\nstring Lexer::DescribeLastError() {\n  if (last_token_) {\n    switch (last_token_[0]) {\n    case '\\t':\n      return \"tabs are not allowed, use spaces\";\n    }\n  }\n  return \"lexing error\";\n}\n\nvoid Lexer::UnreadToken() {\n  ofs_ = last_token_;\n}\n\nLexer::Token Lexer::ReadToken() {\n  const char* p = ofs_;\n  const char* q;\n  const char* start;\n  Lexer::Token token;\n  for (;;) {\n    start = p;\n    /*!re2c\n    re2c:define:YYCTYPE = \"unsigned char\";\n    re2c:define:YYCURSOR = p;\n    re2c:define:YYMARKER = q;\n    re2c:yyfill:enable = 0;\n\n    nul = \"\\000\";\n    simple_varname = [a-zA-Z0-9_-]+;\n    varname = [a-zA-Z0-9_.-]+;\n\n    [ ]*\"#\"[^\\000\\n]*\"\\n\" { continue; }\n    [ ]*\"\\r\\n\" { token = NEWLINE;  break; }\n    [ ]*\"\\n\"   { token = NEWLINE;  break; }\n    [ ]+       { token = INDENT;   break; }\n    \"build\"    { token = BUILD;    break; }\n    \"pool\"     { token = POOL;     break; }\n    \"rule\"     { token = RULE;     break; }\n    \"default\"  { token = DEFAULT;  break; }\n    \"=\"        { token = EQUALS;   break; }\n    \":\"        { token = COLON;    break; }\n    \"|@\"       { token = PIPEAT;   break; }\n    \"||\"       { token = PIPE2;    break; }\n    \"|\"        { token = PIPE;     break; }\n    \"include\"  { token = INCLUDE;  break; }\n    \"subninja\" { token = SUBNINJA; break; }\n    varname    { token = IDENT;    break; }\n    nul        { token = TEOF;     break; }\n    [^]        { token = ERROR;    break; }\n    */\n  }\n\n  last_token_ = start;\n  ofs_ = p;\n  if (token != NEWLINE && token != TEOF)\n    EatWhitespace();\n  return token;\n}\n\nbool Lexer::PeekToken(Token token) {\n  Token t = ReadToken();\n  if (t == token)\n    return true;\n  UnreadToken();\n  return false;\n}\n\nvoid Lexer::EatWhitespace() {\n  const char* p = ofs_;\n  const char* q;\n  for (;;) {\n    ofs_ = p;\n    /*!re2c\n    [ ]+    { continue; }\n    \"$\\r\\n\" { continue; }\n    \"$\\n\"   { continue; }\n    nul     { break; }\n    [^]     { break; }\n    */\n  }\n}\n\nbool Lexer::ReadIdent(string* out) {\n  const char* p = ofs_;\n  const char* start;\n  for (;;) {\n    start = p;\n    /*!re2c\n    varname {\n      out->assign(start, p - start);\n      break;\n    }\n    [^] {\n      last_token_ = start;\n      return false;\n    }\n    */\n  }\n  last_token_ = start;\n  ofs_ = p;\n  EatWhitespace();\n  return true;\n}\n\nbool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) {\n  const char* p = ofs_;\n  const char* q;\n  const char* start;\n  for (;;) {\n    start = p;\n    /*!re2c\n    [^$ :\\r\\n|\\000]+ {\n      eval->AddText(StringPiece(start, p - start));\n      continue;\n    }\n    \"\\r\\n\" {\n      if (path)\n        p = start;\n      break;\n    }\n    [ :|\\n] {\n      if (path) {\n        p = start;\n        break;\n      } else {\n        if (*start == '\\n')\n          break;\n        eval->AddText(StringPiece(start, 1));\n        continue;\n      }\n    }\n    \"$$\" {\n      eval->AddText(StringPiece(\"$\", 1));\n      continue;\n    }\n    \"$ \" {\n      eval->AddText(StringPiece(\" \", 1));\n      continue;\n    }\n    \"$\\r\\n\"[ ]* {\n      continue;\n    }\n    \"$\\n\"[ ]* {\n      continue;\n    }\n    \"${\"varname\"}\" {\n      eval->AddSpecial(StringPiece(start + 2, p - start - 3));\n      continue;\n    }\n    \"$\"simple_varname {\n      eval->AddSpecial(StringPiece(start + 1, p - start - 1));\n      continue;\n    }\n    \"$:\" {\n      eval->AddText(StringPiece(\":\", 1));\n      continue;\n    }\n    \"$^\" {\n      if (!newline_version_checked_)\n      {\n        if ((manifest_version_major < kMinNewlineEscapeVersionMajor) ||\n            (manifest_version_major = kMinNewlineEscapeVersionMajor &&\n             manifest_version_minor < kMinNewlineEscapeVersionMinor))\n        {\n          return Error(\"using $^ escape requires specifying 'ninja_required_version' with version greater or equal 1.14\", err);\n        }\n        newline_version_checked_ = true;\n      }\n      eval->AddText(StringPiece(\"\\n\", 1));\n      continue;\n    }\n    \"$\". {\n      last_token_ = start;\n      return Error(\"bad $-escape (literal $ must be written as $$)\", err);\n    }\n    nul {\n      last_token_ = start;\n      return Error(\"unexpected EOF\", err);\n    }\n    [^] {\n      last_token_ = start;\n      return Error(DescribeLastError(), err);\n    }\n    */\n  }\n  last_token_ = start;\n  ofs_ = p;\n  if (path)\n    EatWhitespace();\n  // Non-path strings end in newlines, so there's no whitespace to eat.\n  return true;\n}\n"
  },
  {
    "path": "src/lexer_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"lexer.h\"\n\n#include \"eval_env.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nTEST(Lexer, ReadVarValue) {\n  Lexer lexer(\"plain text $var $VaR ${x}\\n\");\n  EvalString eval;\n  string err;\n  EXPECT_TRUE(lexer.ReadVarValue(&eval, &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(\"[plain text ][$var][ ][$VaR][ ][$x]\",\n            eval.Serialize());\n}\n\nTEST(Lexer, ReadEvalStringEscapes) {\n  Lexer lexer(\"$ $$ab c$: $\\ncde\\n\");\n  EvalString eval;\n  string err;\n  EXPECT_TRUE(lexer.ReadVarValue(&eval, &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(\"[ $ab c: cde]\",\n            eval.Serialize());\n}\n\nTEST(Lexer, ReadIdent) {\n  Lexer lexer(\"foo baR baz_123 foo-bar\");\n  string ident;\n  EXPECT_TRUE(lexer.ReadIdent(&ident));\n  EXPECT_EQ(\"foo\", ident);\n  EXPECT_TRUE(lexer.ReadIdent(&ident));\n  EXPECT_EQ(\"baR\", ident);\n  EXPECT_TRUE(lexer.ReadIdent(&ident));\n  EXPECT_EQ(\"baz_123\", ident);\n  EXPECT_TRUE(lexer.ReadIdent(&ident));\n  EXPECT_EQ(\"foo-bar\", ident);\n}\n\nTEST(Lexer, ReadIdentCurlies) {\n  // Verify that ReadIdent includes dots in the name,\n  // but in an expansion $bar.dots stops at the dot.\n  Lexer lexer(\"foo.dots $bar.dots ${bar.dots}\\n\");\n  string ident;\n  EXPECT_TRUE(lexer.ReadIdent(&ident));\n  EXPECT_EQ(\"foo.dots\", ident);\n\n  EvalString eval;\n  string err;\n  EXPECT_TRUE(lexer.ReadVarValue(&eval, &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(\"[$bar][.dots ][$bar.dots]\",\n            eval.Serialize());\n}\n\nTEST(Lexer, Error) {\n  Lexer lexer(\"foo$\\nbad $\");\n  EvalString eval;\n  string err;\n  ASSERT_FALSE(lexer.ReadVarValue(&eval, &err));\n  EXPECT_EQ(\"input:2: bad $-escape (literal $ must be written as $$)\\n\"\n            \"bad $\\n\"\n            \"    ^ near here\"\n            , err);\n}\n\nTEST(Lexer, CommentEOF) {\n  // Verify we don't run off the end of the string when the EOF is\n  // mid-comment.\n  Lexer lexer(\"# foo\");\n  Lexer::Token token = lexer.ReadToken();\n  EXPECT_EQ(Lexer::ERROR, token);\n}\n\nTEST(Lexer, Tabs) {\n  // Verify we print a useful error on a disallowed character.\n  Lexer lexer(\"   \\tfoobar\");\n  Lexer::Token token = lexer.ReadToken();\n  EXPECT_EQ(Lexer::INDENT, token);\n  token = lexer.ReadToken();\n  EXPECT_EQ(Lexer::ERROR, token);\n  EXPECT_EQ(\"tabs are not allowed, use spaces\", lexer.DescribeLastError());\n}\n\nTEST(Lexer, EscapedNewlines) {\n  Lexer lexer(\"foo$\\nbar$^newline foo\\n\");\n  EvalString eval;\n  string err;\n  EXPECT_FALSE(lexer.ReadVarValue(&eval, &err));\n  EXPECT_EQ(\"input:1: using $^ escape requires specifying 'ninja_required_version' with version greater or equal 1.14\\n\", err);\n\n  lexer.manifest_version_major = 1;\n  lexer.manifest_version_minor = 14;\n\n  eval.Clear();\n  err = \"\";\n  EXPECT_TRUE(lexer.ReadVarValue(&eval, &err));\n  EXPECT_EQ(\"\", err);\n  EXPECT_EQ(\"[foobar\\nnewline foo]\", eval.Serialize());\n}\n"
  },
  {
    "path": "src/line_printer.cc",
    "content": "// Copyright 2013 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"line_printer.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#ifdef _WIN32\n#include <windows.h>\n#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING\n#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4\n#endif\n#else\n#include <unistd.h>\n#include <sys/ioctl.h>\n#include <termios.h>\n#include <sys/time.h>\n#endif\n\n#include \"elide_middle.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nLinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {\n  const char* term = getenv(\"TERM\");\n#ifndef _WIN32\n  smart_terminal_ = isatty(1) && term && string(term) != \"dumb\";\n#else\n  if (term && string(term) == \"dumb\") {\n    smart_terminal_ = false;\n  } else {\n    console_ = GetStdHandle(STD_OUTPUT_HANDLE);\n    CONSOLE_SCREEN_BUFFER_INFO csbi;\n    smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);\n  }\n#endif\n  supports_color_ = smart_terminal_;\n#ifdef _WIN32\n  // Try enabling ANSI escape sequence support on Windows 10 terminals.\n  if (supports_color_) {\n    DWORD mode;\n    if (GetConsoleMode(console_, &mode)) {\n      if (!SetConsoleMode(console_, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {\n        supports_color_ = false;\n      }\n    }\n  }\n#endif\n  if (!supports_color_) {\n    const char* clicolor_force = getenv(\"CLICOLOR_FORCE\");\n    supports_color_ = clicolor_force && std::string(clicolor_force) != \"0\";\n  }\n}\n\nvoid LinePrinter::Print(string to_print, LineType type) {\n  if (console_locked_) {\n    line_buffer_ = to_print;\n    line_type_ = type;\n    return;\n  }\n\n  if (smart_terminal_) {\n    printf(\"\\r\");  // Print over previous line, if any.\n    // On Windows, calling a C library function writing to stdout also handles\n    // pausing the executable when the \"Pause\" key or Ctrl-S is pressed.\n  }\n\n  if (smart_terminal_ && type == ELIDE) {\n#ifdef _WIN32\n    CONSOLE_SCREEN_BUFFER_INFO csbi;\n    GetConsoleScreenBufferInfo(console_, &csbi);\n\n    ElideMiddleInPlace(to_print, static_cast<size_t>(csbi.dwSize.X));\n    if (supports_color_) {  // this means ENABLE_VIRTUAL_TERMINAL_PROCESSING\n                            // succeeded\n      printf(\"%s\\x1B[K\", to_print.c_str());  // Clear to end of line.\n      fflush(stdout);\n    } else {\n      // We don't want to have the cursor spamming back and forth, so instead of\n      // printf use WriteConsoleOutput which updates the contents of the buffer,\n      // but doesn't move the cursor position.\n      COORD buf_size = { csbi.dwSize.X, 1 };\n      COORD zero_zero = { 0, 0 };\n      SMALL_RECT target = { csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,\n                            static_cast<SHORT>(csbi.dwCursorPosition.X +\n                                               csbi.dwSize.X - 1),\n                            csbi.dwCursorPosition.Y };\n      vector<CHAR_INFO> char_data(csbi.dwSize.X);\n      for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {\n        char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';\n        char_data[i].Attributes = csbi.wAttributes;\n      }\n      WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);\n    }\n#else\n    // Limit output to width of the terminal if provided so we don't cause\n    // line-wrapping.\n    winsize size;\n    if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0) && size.ws_col) {\n      ElideMiddleInPlace(to_print, size.ws_col);\n    }\n    printf(\"%s\", to_print.c_str());\n    printf(\"\\x1B[K\");  // Clear to end of line.\n    fflush(stdout);\n#endif\n\n    have_blank_line_ = false;\n  } else {\n    printf(\"%s\\n\", to_print.c_str());\n    fflush(stdout);\n  }\n}\n\nvoid LinePrinter::PrintOrBuffer(const char* data, size_t size) {\n  if (console_locked_) {\n    output_buffer_.append(data, size);\n  } else {\n    // Avoid printf and C strings, since the actual output might contain null\n    // bytes like UTF-16 does (yuck).\n    fwrite(data, 1, size, stdout);\n  }\n}\n\nvoid LinePrinter::PrintOnNewLine(const string& to_print) {\n  if (console_locked_ && !line_buffer_.empty()) {\n    output_buffer_.append(line_buffer_);\n    output_buffer_.append(1, '\\n');\n    line_buffer_.clear();\n  }\n  if (!have_blank_line_) {\n    PrintOrBuffer(\"\\n\", 1);\n  }\n  if (!to_print.empty()) {\n    PrintOrBuffer(&to_print[0], to_print.size());\n  }\n  have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\\n';\n}\n\nvoid LinePrinter::SetConsoleLocked(bool locked) {\n  if (locked == console_locked_)\n    return;\n\n  if (locked)\n    PrintOnNewLine(\"\");\n\n  console_locked_ = locked;\n\n  if (!locked) {\n    PrintOnNewLine(output_buffer_);\n    if (!line_buffer_.empty()) {\n      Print(line_buffer_, line_type_);\n    }\n    output_buffer_.clear();\n    line_buffer_.clear();\n  }\n}\n"
  },
  {
    "path": "src/line_printer.h",
    "content": "// Copyright 2013 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_LINE_PRINTER_H_\n#define NINJA_LINE_PRINTER_H_\n\n#include <stddef.h>\n#include <string>\n\n/// Prints lines of text, possibly overprinting previously printed lines\n/// if the terminal supports it.\nstruct LinePrinter {\n  LinePrinter();\n\n  bool is_smart_terminal() const { return smart_terminal_; }\n  void set_smart_terminal(bool smart) { smart_terminal_ = smart; }\n\n  bool supports_color() const { return supports_color_; }\n\n  enum LineType {\n    FULL,\n    ELIDE\n  };\n  /// Overprints the current line. If type is ELIDE, elides to_print to fit on\n  /// one line.\n  void Print(std::string to_print, LineType type);\n\n  /// Prints a string on a new line, not overprinting previous output.\n  void PrintOnNewLine(const std::string& to_print);\n\n  /// Lock or unlock the console.  Any output sent to the LinePrinter while the\n  /// console is locked will not be printed until it is unlocked.\n  void SetConsoleLocked(bool locked);\n\n private:\n  /// Whether we can do fancy terminal control codes.\n  bool smart_terminal_;\n\n  /// Whether we can use ISO 6429 (ANSI) color sequences.\n  bool supports_color_;\n\n  /// Whether the caret is at the beginning of a blank line.\n  bool have_blank_line_;\n\n  /// Whether console is locked.\n  bool console_locked_;\n\n  /// Buffered current line while console is locked.\n  std::string line_buffer_;\n\n  /// Buffered line type while console is locked.\n  LineType line_type_;\n\n  /// Buffered console output while console is locked.\n  std::string output_buffer_;\n\n#ifdef _WIN32\n  void* console_;\n#endif\n\n  /// Print the given data to the console, or buffer it if it is locked.\n  void PrintOrBuffer(const char *data, size_t size);\n};\n\n#endif  // NINJA_LINE_PRINTER_H_\n"
  },
  {
    "path": "src/load_status.h",
    "content": "// Copyright 2019 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_LOAD_STATUS_H_\n#define NINJA_LOAD_STATUS_H_\n\nenum LoadStatus {\n  LOAD_ERROR,\n  LOAD_SUCCESS,\n  LOAD_NOT_FOUND,\n};\n\n#endif  // NINJA_LOAD_STATUS_H_\n"
  },
  {
    "path": "src/manifest_parser.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"manifest_parser.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <charconv>\n#include <memory>\n#include <vector>\n\n#include \"graph.h\"\n#include \"state.h\"\n#include \"util.h\"\n#include \"version.h\"\n\nusing namespace std;\n\nManifestParser::ManifestParser(State* state, FileReader* file_reader,\n                               ManifestParserOptions options)\n    : Parser(state, file_reader),\n      options_(options), quiet_(false) {\n  env_ = &state->bindings_;\n}\n\nbool ManifestParser::Parse(const string& filename, const string& input,\n                           string* err) {\n  lexer_.Start(filename, input);\n\n  for (;;) {\n    Lexer::Token token = lexer_.ReadToken();\n    switch (token) {\n    case Lexer::POOL:\n      if (!ParsePool(err))\n        return false;\n      break;\n    case Lexer::BUILD:\n      if (!ParseEdge(err))\n        return false;\n      break;\n    case Lexer::RULE:\n      if (!ParseRule(err))\n        return false;\n      break;\n    case Lexer::DEFAULT:\n      if (!ParseDefault(err))\n        return false;\n      break;\n    case Lexer::IDENT: {\n      lexer_.UnreadToken();\n      string name;\n      EvalString let_value;\n      if (!ParseLet(&name, &let_value, err))\n        return false;\n      string value = let_value.Evaluate(env_);\n      // Check ninja_required_version immediately so we can exit\n      // before encountering any syntactic surprises.\n      if (name == \"ninja_required_version\")\n        CheckNinjaVersion(value, &lexer_.manifest_version_major, &lexer_.manifest_version_minor);\n      env_->AddBinding(name, value);\n      break;\n    }\n    case Lexer::INCLUDE:\n      if (!ParseFileInclude(false, err))\n        return false;\n      break;\n    case Lexer::SUBNINJA:\n      if (!ParseFileInclude(true, err))\n        return false;\n      break;\n    case Lexer::ERROR: {\n      return lexer_.Error(lexer_.DescribeLastError(), err);\n    }\n    case Lexer::TEOF:\n      return true;\n    case Lexer::NEWLINE:\n      break;\n    default:\n      return lexer_.Error(string(\"unexpected \") + Lexer::TokenName(token),\n                          err);\n    }\n  }\n  return false;  // not reached\n}\n\n\nbool ManifestParser::ParsePool(string* err) {\n  string name;\n  if (!lexer_.ReadIdent(&name))\n    return lexer_.Error(\"expected pool name\", err);\n\n  if (!ExpectToken(Lexer::NEWLINE, err))\n    return false;\n\n  if (state_->LookupPool(name) != NULL)\n    return lexer_.Error(\"duplicate pool '\" + name + \"'\", err);\n\n  int depth = -1;\n\n  while (lexer_.PeekToken(Lexer::INDENT)) {\n    string key;\n    EvalString value;\n    if (!ParseLet(&key, &value, err))\n      return false;\n\n    if (key == \"depth\") {\n      string depth_string = value.Evaluate(env_);\n      const char* begin = depth_string.data();\n      const char* end = begin + depth_string.size();\n      auto result = std::from_chars(begin, end, depth);\n      if (result.ec != std::errc() || result.ptr != end || depth < 0) {\n        return lexer_.Error(\"invalid pool depth\", err);\n      }\n    } else {\n      return lexer_.Error(\"unexpected variable '\" + key + \"'\", err);\n    }\n  }\n\n  if (depth < 0)\n    return lexer_.Error(\"expected 'depth =' line\", err);\n\n  state_->AddPool(new Pool(name, depth));\n  return true;\n}\n\n\nbool ManifestParser::ParseRule(string* err) {\n  string name;\n  if (!lexer_.ReadIdent(&name))\n    return lexer_.Error(\"expected rule name\", err);\n\n  if (!ExpectToken(Lexer::NEWLINE, err))\n    return false;\n\n  if (env_->LookupRuleCurrentScope(name) != NULL)\n    return lexer_.Error(\"duplicate rule '\" + name + \"'\", err);\n\n  auto rule = std::unique_ptr<Rule>(new Rule(name));\n\n  while (lexer_.PeekToken(Lexer::INDENT)) {\n    string key;\n    EvalString value;\n    if (!ParseLet(&key, &value, err))\n      return false;\n\n    if (Rule::IsReservedBinding(key)) {\n      rule->AddBinding(key, value);\n    } else {\n      // Die on other keyvals for now; revisit if we want to add a\n      // scope here.\n      return lexer_.Error(\"unexpected variable '\" + key + \"'\", err);\n    }\n  }\n\n  if (rule->bindings_[\"rspfile\"].empty() !=\n      rule->bindings_[\"rspfile_content\"].empty()) {\n    return lexer_.Error(\"rspfile and rspfile_content need to be \"\n                        \"both specified\", err);\n  }\n\n  if (rule->bindings_[\"command\"].empty())\n    return lexer_.Error(\"expected 'command =' line\", err);\n\n  env_->AddRule(std::move(rule));\n  return true;\n}\n\nbool ManifestParser::ParseLet(string* key, EvalString* value, string* err) {\n  if (!lexer_.ReadIdent(key))\n    return lexer_.Error(\"expected variable name\", err);\n  if (!ExpectToken(Lexer::EQUALS, err))\n    return false;\n  if (!lexer_.ReadVarValue(value, err))\n    return false;\n  return true;\n}\n\nbool ManifestParser::ParseDefault(string* err) {\n  EvalString eval;\n  if (!lexer_.ReadPath(&eval, err))\n    return false;\n  if (eval.empty())\n    return lexer_.Error(\"expected target name\", err);\n\n  do {\n    string path = eval.Evaluate(env_);\n    if (path.empty())\n      return lexer_.Error(\"empty path\", err);\n    uint64_t slash_bits;  // Unused because this only does lookup.\n    CanonicalizePath(&path, &slash_bits);\n    std::string default_err;\n    if (!state_->AddDefault(path, &default_err))\n      return lexer_.Error(default_err, err);\n\n    eval.Clear();\n    if (!lexer_.ReadPath(&eval, err))\n      return false;\n  } while (!eval.empty());\n\n  return ExpectToken(Lexer::NEWLINE, err);\n}\n\nbool ManifestParser::ParseEdge(string* err) {\n  ins_.clear();\n  outs_.clear();\n  validations_.clear();\n\n  {\n    EvalString out;\n    if (!lexer_.ReadPath(&out, err))\n      return false;\n    while (!out.empty()) {\n      outs_.push_back(std::move(out));\n\n      out.Clear();\n      if (!lexer_.ReadPath(&out, err))\n        return false;\n    }\n  }\n\n  // Add all implicit outs, counting how many as we go.\n  int implicit_outs = 0;\n  if (lexer_.PeekToken(Lexer::PIPE)) {\n    for (;;) {\n      EvalString out;\n      if (!lexer_.ReadPath(&out, err))\n        return false;\n      if (out.empty())\n        break;\n      outs_.push_back(std::move(out));\n      ++implicit_outs;\n    }\n  }\n\n  if (outs_.empty())\n    return lexer_.Error(\"expected path\", err);\n\n  if (!ExpectToken(Lexer::COLON, err))\n    return false;\n\n  string rule_name;\n  if (!lexer_.ReadIdent(&rule_name))\n    return lexer_.Error(\"expected build command name\", err);\n\n  const Rule* rule = env_->LookupRule(rule_name);\n  if (!rule)\n    return lexer_.Error(\"unknown build rule '\" + rule_name + \"'\", err);\n\n  for (;;) {\n    // XXX should we require one path here?\n    EvalString in;\n    if (!lexer_.ReadPath(&in, err))\n      return false;\n    if (in.empty())\n      break;\n    ins_.push_back(std::move(in));\n  }\n\n  // Add all implicit deps, counting how many as we go.\n  int implicit = 0;\n  if (lexer_.PeekToken(Lexer::PIPE)) {\n    for (;;) {\n      EvalString in;\n      if (!lexer_.ReadPath(&in, err))\n        return false;\n      if (in.empty())\n        break;\n      ins_.push_back(std::move(in));\n      ++implicit;\n    }\n  }\n\n  // Add all order-only deps, counting how many as we go.\n  int order_only = 0;\n  if (lexer_.PeekToken(Lexer::PIPE2)) {\n    for (;;) {\n      EvalString in;\n      if (!lexer_.ReadPath(&in, err))\n        return false;\n      if (in.empty())\n        break;\n      ins_.push_back(std::move(in));\n      ++order_only;\n    }\n  }\n\n  // Add all validations, counting how many as we go.\n  if (lexer_.PeekToken(Lexer::PIPEAT)) {\n    for (;;) {\n      EvalString validation;\n      if (!lexer_.ReadPath(&validation, err))\n        return false;\n      if (validation.empty())\n        break;\n      validations_.push_back(std::move(validation));\n    }\n  }\n\n  if (!ExpectToken(Lexer::NEWLINE, err))\n    return false;\n\n  // Bindings on edges are rare, so allocate per-edge envs only when needed.\n  bool has_indent_token = lexer_.PeekToken(Lexer::INDENT);\n  BindingEnv* env = has_indent_token ? new BindingEnv(env_) : env_;\n  while (has_indent_token) {\n    string key;\n    EvalString val;\n    if (!ParseLet(&key, &val, err))\n      return false;\n\n    env->AddBinding(key, val.Evaluate(env_));\n    has_indent_token = lexer_.PeekToken(Lexer::INDENT);\n  }\n\n  Edge* edge = state_->AddEdge(rule);\n  edge->env_ = env;\n\n  string pool_name = edge->GetBinding(\"pool\");\n  if (!pool_name.empty()) {\n    Pool* pool = state_->LookupPool(pool_name);\n    if (pool == NULL)\n      return lexer_.Error(\"unknown pool name '\" + pool_name + \"'\", err);\n    edge->pool_ = pool;\n  }\n\n  edge->outputs_.reserve(outs_.size());\n  for (size_t i = 0, e = outs_.size(); i != e; ++i) {\n    string path = outs_[i].Evaluate(env);\n    if (path.empty())\n      return lexer_.Error(\"empty path\", err);\n    uint64_t slash_bits;\n    CanonicalizePath(&path, &slash_bits);\n    if (!state_->AddOut(edge, path, slash_bits, err)) {\n      lexer_.Error(std::string(*err), err);\n      return false;\n    }\n  }\n\n  if (edge->outputs_.empty()) {\n    // All outputs of the edge are already created by other edges. Don't add\n    // this edge.  Do this check before input nodes are connected to the edge.\n    state_->edges_.pop_back();\n    delete edge;\n    return true;\n  }\n  edge->implicit_outs_ = implicit_outs;\n\n  edge->inputs_.reserve(ins_.size());\n  for (vector<EvalString>::iterator i = ins_.begin(); i != ins_.end(); ++i) {\n    string path = i->Evaluate(env);\n    if (path.empty())\n      return lexer_.Error(\"empty path\", err);\n    uint64_t slash_bits;\n    CanonicalizePath(&path, &slash_bits);\n    state_->AddIn(edge, path, slash_bits);\n  }\n  edge->implicit_deps_ = implicit;\n  edge->order_only_deps_ = order_only;\n\n  edge->validations_.reserve(validations_.size());\n  for (std::vector<EvalString>::iterator v = validations_.begin();\n      v != validations_.end(); ++v) {\n    string path = v->Evaluate(env);\n    if (path.empty())\n      return lexer_.Error(\"empty path\", err);\n    uint64_t slash_bits;\n    CanonicalizePath(&path, &slash_bits);\n    state_->AddValidation(edge, path, slash_bits);\n  }\n\n  if (options_.phony_cycle_action_ == kPhonyCycleActionWarn &&\n      edge->maybe_phonycycle_diagnostic()) {\n    // CMake 2.8.12.x and 3.0.x incorrectly write phony build statements\n    // that reference themselves.  Ninja used to tolerate these in the\n    // build graph but that has since been fixed.  Filter them out to\n    // support users of those old CMake versions.\n    Node* out = edge->outputs_[0];\n    vector<Node*>::iterator new_end =\n        remove(edge->inputs_.begin(), edge->inputs_.end(), out);\n    if (new_end != edge->inputs_.end()) {\n      edge->inputs_.erase(new_end, edge->inputs_.end());\n      if (!quiet_) {\n        Warning(\"phony target '%s' names itself as an input; \"\n                \"ignoring [-w phonycycle=warn]\",\n                out->path().c_str());\n      }\n    }\n  }\n\n  // Lookup, validate, and save any dyndep binding.  It will be used later\n  // to load generated dependency information dynamically, but it must\n  // be one of our manifest-specified inputs.\n  string dyndep = edge->GetUnescapedDyndep();\n  if (!dyndep.empty()) {\n    uint64_t slash_bits;\n    CanonicalizePath(&dyndep, &slash_bits);\n    edge->dyndep_ = state_->GetNode(dyndep, slash_bits);\n    edge->dyndep_->set_dyndep_pending(true);\n    vector<Node*>::iterator dgi =\n      std::find(edge->inputs_.begin(), edge->inputs_.end(), edge->dyndep_);\n    if (dgi == edge->inputs_.end()) {\n      return lexer_.Error(\"dyndep '\" + dyndep + \"' is not an input\", err);\n    }\n    assert(!edge->dyndep_->generated_by_dep_loader());\n  }\n\n  return true;\n}\n\nbool ManifestParser::ParseFileInclude(bool new_scope, string* err) {\n  EvalString eval;\n  if (!lexer_.ReadPath(&eval, err))\n    return false;\n  string path = eval.Evaluate(env_);\n\n  if (subparser_ == nullptr) {\n    subparser_.reset(new ManifestParser(state_, file_reader_, options_));\n  }\n  if (new_scope) {\n    subparser_->env_ = new BindingEnv(env_);\n  } else {\n    subparser_->env_ = env_;\n  }\n\n  if (!subparser_->Load(path, err, &lexer_))\n    return false;\n\n  if (!ExpectToken(Lexer::NEWLINE, err))\n    return false;\n\n  return true;\n}\n"
  },
  {
    "path": "src/manifest_parser.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_MANIFEST_PARSER_H_\n#define NINJA_MANIFEST_PARSER_H_\n\n#include \"parser.h\"\n\n#include <memory>\n#include <vector>\n\nstruct BindingEnv;\nstruct EvalString;\n\nenum DupeEdgeAction {\n  kDupeEdgeActionWarn,\n  kDupeEdgeActionError,\n};\n\nenum PhonyCycleAction {\n  kPhonyCycleActionWarn,\n  kPhonyCycleActionError,\n};\n\nstruct ManifestParserOptions {\n  PhonyCycleAction phony_cycle_action_ = kPhonyCycleActionWarn;\n};\n\n/// Parses .ninja files.\nstruct ManifestParser : public Parser {\n  ManifestParser(State* state, FileReader* file_reader,\n                 ManifestParserOptions options = ManifestParserOptions());\n\n  /// Parse a text string of input.  Used by tests.\n  bool ParseTest(const std::string& input, std::string* err) {\n    quiet_ = true;\n    return Parse(\"input\", input, err);\n  }\n\nprivate:\n  /// Parse a file, given its contents as a string.\n  bool Parse(const std::string& filename, const std::string& input,\n             std::string* err);\n\n  /// Parse various statement types.\n  bool ParsePool(std::string* err);\n  bool ParseRule(std::string* err);\n  bool ParseLet(std::string* key, EvalString* val, std::string* err);\n  bool ParseEdge(std::string* err);\n  bool ParseDefault(std::string* err);\n\n  /// Parse either a 'subninja' or 'include' line.\n  bool ParseFileInclude(bool new_scope, std::string* err);\n\n  BindingEnv* env_;\n  ManifestParserOptions options_;\n  bool quiet_;\n\n  // ins_/out_/validations_ are reused across invocations to ParseEdge(),\n  // to save on the otherwise constant memory reallocation.\n  // subparser_ is reused solely to get better reuse out ins_/outs_/validation_.\n  std::unique_ptr<ManifestParser> subparser_;\n  std::vector<EvalString> ins_, outs_, validations_;\n};\n\n#endif  // NINJA_MANIFEST_PARSER_H_\n"
  },
  {
    "path": "src/manifest_parser_perftest.cc",
    "content": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Tests manifest parser performance.  Expects to be run in ninja's root\n// directory.\n\n#include <numeric>\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef _WIN32\n#include \"getopt.h\"\n#include <direct.h>\n#elif defined(_AIX)\n#include \"getopt.h\"\n#include <unistd.h>\n#else\n#include <getopt.h>\n#include <unistd.h>\n#endif\n\n#include \"disk_interface.h\"\n#include \"graph.h\"\n#include \"manifest_parser.h\"\n#include \"metrics.h\"\n#include \"state.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nbool WriteFakeManifests(const string& dir, string* err) {\n  RealDiskInterface disk_interface;\n  TimeStamp mtime = disk_interface.Stat(dir + \"/build.ninja\", err);\n  if (mtime != 0)  // 0 means that the file doesn't exist yet.\n    return mtime != -1;\n\n  string command = \"python misc/write_fake_manifests.py \" + dir;\n  printf(\"Creating manifest data...\"); fflush(stdout);\n  int exit_code = system(command.c_str());\n  printf(\"done.\\n\");\n  if (exit_code != 0)\n    *err = \"Failed to run \" + command;\n  return exit_code == 0;\n}\n\nint LoadManifests(bool measure_command_evaluation) {\n  string err;\n  RealDiskInterface disk_interface;\n  State state;\n  ManifestParser parser(&state, &disk_interface);\n  if (!parser.Load(\"build.ninja\", &err)) {\n    fprintf(stderr, \"Failed to read test data: %s\\n\", err.c_str());\n    exit(1);\n  }\n  // Doing an empty build involves reading the manifest and evaluating all\n  // commands required for the requested targets. So include command\n  // evaluation in the perftest by default.\n  int optimization_guard = 0;\n  if (measure_command_evaluation)\n    for (size_t i = 0; i < state.edges_.size(); ++i)\n      optimization_guard += state.edges_[i]->EvaluateCommand().size();\n  return optimization_guard;\n}\n\nint main(int argc, char* argv[]) {\n  bool measure_command_evaluation = true;\n  int opt;\n  while ((opt = getopt(argc, argv, const_cast<char*>(\"fh\"))) != -1) {\n    switch (opt) {\n    case 'f':\n      measure_command_evaluation = false;\n      break;\n    case 'h':\n    default:\n      printf(\"usage: manifest_parser_perftest\\n\"\n\"\\n\"\n\"options:\\n\"\n\"  -f     only measure manifest load time, not command evaluation time\\n\"\n             );\n    return 1;\n    }\n  }\n\n  const char kManifestDir[] = \"build/manifest_perftest\";\n\n  string err;\n  if (!WriteFakeManifests(kManifestDir, &err)) {\n    fprintf(stderr, \"Failed to write test data: %s\\n\", err.c_str());\n    return 1;\n  }\n\n  if (chdir(kManifestDir) < 0)\n    Fatal(\"chdir: %s\", strerror(errno));\n\n  const int kNumRepetitions = 5;\n  vector<int> times;\n  for (int i = 0; i < kNumRepetitions; ++i) {\n    int64_t start = GetTimeMillis();\n    int optimization_guard = LoadManifests(measure_command_evaluation);\n    int delta = (int)(GetTimeMillis() - start);\n    printf(\"%dms (hash: %x)\\n\", delta, optimization_guard);\n    times.push_back(delta);\n  }\n\n  int min = *min_element(times.begin(), times.end());\n  int max = *max_element(times.begin(), times.end());\n  float total = accumulate(times.begin(), times.end(), 0.0f);\n  printf(\"min %dms  max %dms  avg %.1fms\\n\", min, max, total / times.size());\n}\n"
  },
  {
    "path": "src/manifest_parser_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"manifest_parser.h\"\n\n#include <map>\n#include <vector>\n\n#include \"graph.h\"\n#include \"state.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nstruct ParserTest : public testing::Test {\n  void AssertParse(const char* input) {\n    ManifestParser parser(&state, &fs_);\n    string err;\n    EXPECT_TRUE(parser.ParseTest(input, &err));\n    ASSERT_EQ(\"\", err);\n    VerifyGraph(state);\n  }\n\n  State state;\n  VirtualFileSystem fs_;\n};\n\nTEST_F(ParserTest, Empty) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\"\"));\n}\n\nTEST_F(ParserTest, Rules) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"\\n\"\n\"rule date\\n\"\n\"  command = date > $out\\n\"\n\"\\n\"\n\"build result: cat in_1.cc in-2.O\\n\"));\n\n  ASSERT_EQ(3u, state.bindings_.GetRules().size());\n  const auto& rule = state.bindings_.GetRules().begin()->second;\n  EXPECT_EQ(\"cat\", rule->name());\n  EXPECT_EQ(\"[cat ][$in][ > ][$out]\",\n            rule->GetBinding(\"command\")->Serialize());\n}\n\nTEST_F(ParserTest, RuleAttributes) {\n  // Check that all of the allowed rule attributes are parsed ok.\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = a\\n\"\n\"  depfile = a\\n\"\n\"  deps = a\\n\"\n\"  description = a\\n\"\n\"  generator = a\\n\"\n\"  restat = a\\n\"\n\"  rspfile = a\\n\"\n\"  rspfile_content = a\\n\"\n));\n}\n\nTEST_F(ParserTest, IgnoreIndentedComments) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"  #indented comment\\n\"\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"  #generator = 1\\n\"\n\"  restat = 1 # comment\\n\"\n\"  #comment\\n\"\n\"build result: cat in_1.cc in-2.O\\n\"\n\"  #comment\\n\"));\n\n  ASSERT_EQ(2u, state.bindings_.GetRules().size());\n  const auto& rule = state.bindings_.GetRules().begin()->second;\n  EXPECT_EQ(\"cat\", rule->name());\n  Edge* edge = state.GetNode(\"result\", 0)->in_edge();\n  EXPECT_TRUE(edge->GetBindingBool(\"restat\"));\n  EXPECT_FALSE(edge->GetBindingBool(\"generator\"));\n}\n\nTEST_F(ParserTest, IgnoreIndentedBlankLines) {\n  // the indented blanks used to cause parse errors\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"  \\n\"\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"  \\n\"\n\"build result: cat in_1.cc in-2.O\\n\"\n\"  \\n\"\n\"variable=1\\n\"));\n\n  // the variable must be in the top level environment\n  EXPECT_EQ(\"1\", state.bindings_.LookupVariable(\"variable\"));\n}\n\nTEST_F(ParserTest, ResponseFiles) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat_rsp\\n\"\n\"  command = cat $rspfile > $out\\n\"\n\"  rspfile = $rspfile\\n\"\n\"  rspfile_content = $in\\n\"\n\"\\n\"\n\"build out: cat_rsp in\\n\"\n\"  rspfile=out.rsp\\n\"));\n\n  ASSERT_EQ(2u, state.bindings_.GetRules().size());\n  const auto& rule = state.bindings_.GetRules().begin()->second;\n  EXPECT_EQ(\"cat_rsp\", rule->name());\n  EXPECT_EQ(\"[cat ][$rspfile][ > ][$out]\",\n            rule->GetBinding(\"command\")->Serialize());\n  EXPECT_EQ(\"[$rspfile]\", rule->GetBinding(\"rspfile\")->Serialize());\n  EXPECT_EQ(\"[$in]\", rule->GetBinding(\"rspfile_content\")->Serialize());\n}\n\nTEST_F(ParserTest, InNewline) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat_rsp\\n\"\n\"  command = cat $in_newline > $out\\n\"\n\"\\n\"\n\"build out: cat_rsp in in2\\n\"\n\"  rspfile=out.rsp\\n\"));\n\n  ASSERT_EQ(2u, state.bindings_.GetRules().size());\n  const auto& rule = state.bindings_.GetRules().begin()->second;\n  EXPECT_EQ(\"cat_rsp\", rule->name());\n  EXPECT_EQ(\"[cat ][$in_newline][ > ][$out]\",\n            rule->GetBinding(\"command\")->Serialize());\n\n  Edge* edge = state.edges_[0];\n  EXPECT_EQ(\"cat in\\nin2 > out\", edge->EvaluateCommand());\n}\n\nTEST_F(ParserTest, Variables) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"l = one-letter-test\\n\"\n\"rule link\\n\"\n\"  command = ld $l $extra $with_under -o $out $in\\n\"\n\"\\n\"\n\"extra = -pthread\\n\"\n\"with_under = -under\\n\"\n\"build a: link b c\\n\"\n\"nested1 = 1\\n\"\n\"nested2 = $nested1/2\\n\"\n\"build supernested: link x\\n\"\n\"  extra = $nested2/3\\n\"));\n\n  ASSERT_EQ(2u, state.edges_.size());\n  Edge* edge = state.edges_[0];\n  EXPECT_EQ(\"ld one-letter-test -pthread -under -o a b c\",\n            edge->EvaluateCommand());\n  EXPECT_EQ(\"1/2\", state.bindings_.LookupVariable(\"nested2\"));\n\n  edge = state.edges_[1];\n  EXPECT_EQ(\"ld one-letter-test 1/2/3 -under -o supernested x\",\n            edge->EvaluateCommand());\n}\n\nTEST_F(ParserTest, VariableScope) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"foo = bar\\n\"\n\"rule cmd\\n\"\n\"  command = cmd $foo $in $out\\n\"\n\"\\n\"\n\"build inner: cmd a\\n\"\n\"  foo = baz\\n\"\n\"build outer: cmd b\\n\"\n\"\\n\"  // Extra newline after build line tickles a regression.\n));\n\n  ASSERT_EQ(2u, state.edges_.size());\n  EXPECT_EQ(\"cmd baz a inner\", state.edges_[0]->EvaluateCommand());\n  EXPECT_EQ(\"cmd bar b outer\", state.edges_[1]->EvaluateCommand());\n}\n\nTEST_F(ParserTest, Continuation) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule link\\n\"\n\"  command = foo bar $\\n\"\n\"    baz\\n\"\n\"\\n\"\n\"build a: link c $\\n\"\n\" d e f\\n\"));\n\n  ASSERT_EQ(2u, state.bindings_.GetRules().size());\n  const auto& rule = state.bindings_.GetRules().begin()->second;\n  EXPECT_EQ(\"link\", rule->name());\n  EXPECT_EQ(\"[foo bar baz]\", rule->GetBinding(\"command\")->Serialize());\n}\n\nTEST_F(ParserTest, Backslash) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"foo = bar\\\\baz\\n\"\n\"foo2 = bar\\\\ baz\\n\"\n));\n  EXPECT_EQ(\"bar\\\\baz\", state.bindings_.LookupVariable(\"foo\"));\n  EXPECT_EQ(\"bar\\\\ baz\", state.bindings_.LookupVariable(\"foo2\"));\n}\n\nTEST_F(ParserTest, Comment) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"# this is a comment\\n\"\n\"foo = not # a comment\\n\"));\n  EXPECT_EQ(\"not # a comment\", state.bindings_.LookupVariable(\"foo\"));\n}\n\nTEST_F(ParserTest, Dollars) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule foo\\n\"\n\"  command = ${out}bar$$baz$$$\\n\"\n\"blah\\n\"\n\"x = $$dollar\\n\"\n\"build $x: foo y\\n\"\n));\n  EXPECT_EQ(\"$dollar\", state.bindings_.LookupVariable(\"x\"));\n#ifdef _WIN32\n  EXPECT_EQ(\"$dollarbar$baz$blah\", state.edges_[0]->EvaluateCommand());\n#else\n  EXPECT_EQ(\"'$dollar'bar$baz$blah\", state.edges_[0]->EvaluateCommand());\n#endif\n}\n\nTEST_F(ParserTest, EscapeSpaces) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule spaces\\n\"\n\"  command = something\\n\"\n\"build foo$ bar: spaces $$one two$$$ three\\n\"\n));\n  EXPECT_TRUE(state.LookupNode(\"foo bar\"));\n  EXPECT_EQ(state.edges_[0]->outputs_[0]->path(), \"foo bar\");\n  EXPECT_EQ(state.edges_[0]->inputs_[0]->path(), \"$one\");\n  EXPECT_EQ(state.edges_[0]->inputs_[1]->path(), \"two$ three\");\n  EXPECT_EQ(state.edges_[0]->EvaluateCommand(), \"something\");\n}\n\nTEST_F(ParserTest, CanonicalizeFile) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build out: cat in/1 in//2\\n\"\n\"build in/1: cat\\n\"\n\"build in/2: cat\\n\"));\n\n  EXPECT_TRUE(state.LookupNode(\"in/1\"));\n  EXPECT_TRUE(state.LookupNode(\"in/2\"));\n  EXPECT_FALSE(state.LookupNode(\"in//1\"));\n  EXPECT_FALSE(state.LookupNode(\"in//2\"));\n}\n\n#ifdef _WIN32\nTEST_F(ParserTest, CanonicalizeFileBackslashes) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build out: cat in\\\\1 in\\\\\\\\2\\n\"\n\"build in\\\\1: cat\\n\"\n\"build in\\\\2: cat\\n\"));\n\n  Node* node = state.LookupNode(\"in/1\");;\n  EXPECT_TRUE(node);\n  EXPECT_EQ(1, node->slash_bits());\n  node = state.LookupNode(\"in/2\");\n  EXPECT_TRUE(node);\n  EXPECT_EQ(1, node->slash_bits());\n  EXPECT_FALSE(state.LookupNode(\"in//1\"));\n  EXPECT_FALSE(state.LookupNode(\"in//2\"));\n}\n#endif\n\nTEST_F(ParserTest, PathVariables) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"dir = out\\n\"\n\"build $dir/exe: cat src\\n\"));\n\n  EXPECT_FALSE(state.LookupNode(\"$dir/exe\"));\n  EXPECT_TRUE(state.LookupNode(\"out/exe\"));\n}\n\nTEST_F(ParserTest, CanonicalizePaths) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build ./out.o: cat ./bar/baz/../foo.cc\\n\"));\n\n  EXPECT_FALSE(state.LookupNode(\"./out.o\"));\n  EXPECT_TRUE(state.LookupNode(\"out.o\"));\n  EXPECT_FALSE(state.LookupNode(\"./bar/baz/../foo.cc\"));\n  EXPECT_TRUE(state.LookupNode(\"bar/foo.cc\"));\n}\n\n#ifdef _WIN32\nTEST_F(ParserTest, CanonicalizePathsBackslashes) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build ./out.o: cat ./bar/baz/../foo.cc\\n\"\n\"build .\\\\out2.o: cat .\\\\bar/baz\\\\..\\\\foo.cc\\n\"\n\"build .\\\\out3.o: cat .\\\\bar\\\\baz\\\\..\\\\foo3.cc\\n\"\n));\n\n  EXPECT_FALSE(state.LookupNode(\"./out.o\"));\n  EXPECT_FALSE(state.LookupNode(\".\\\\out2.o\"));\n  EXPECT_FALSE(state.LookupNode(\".\\\\out3.o\"));\n  EXPECT_TRUE(state.LookupNode(\"out.o\"));\n  EXPECT_TRUE(state.LookupNode(\"out2.o\"));\n  EXPECT_TRUE(state.LookupNode(\"out3.o\"));\n  EXPECT_FALSE(state.LookupNode(\"./bar/baz/../foo.cc\"));\n  EXPECT_FALSE(state.LookupNode(\".\\\\bar/baz\\\\..\\\\foo.cc\"));\n  EXPECT_FALSE(state.LookupNode(\".\\\\bar/baz\\\\..\\\\foo3.cc\"));\n  Node* node = state.LookupNode(\"bar/foo.cc\");\n  EXPECT_TRUE(node);\n  EXPECT_EQ(0, node->slash_bits());\n  node = state.LookupNode(\"bar/foo3.cc\");\n  EXPECT_TRUE(node);\n  EXPECT_EQ(1, node->slash_bits());\n}\n#endif\n\nTEST_F(ParserTest, DuplicateEdgeWithMultipleOutputsError) {\n  const char kInput[] =\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build out1 out2: cat in1\\n\"\n\"build out1: cat in2\\n\"\n\"build final: cat out1\\n\";\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:5: multiple rules generate out1\\n\", err);\n}\n\nTEST_F(ParserTest, DuplicateEdgeInIncludedFile) {\n  fs_.Create(\"sub.ninja\",\n    \"rule cat\\n\"\n    \"  command = cat $in > $out\\n\"\n    \"build out1 out2: cat in1\\n\"\n    \"build out1: cat in2\\n\"\n    \"build final: cat out1\\n\");\n  const char kInput[] =\n    \"subninja sub.ninja\\n\";\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"sub.ninja:5: multiple rules generate out1\\n\", err);\n}\n\nTEST_F(ParserTest, PhonySelfReferenceIgnored) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"build a: phony a\\n\"\n));\n\n  Node* node = state.LookupNode(\"a\");\n  Edge* edge = node->in_edge();\n  ASSERT_TRUE(edge->inputs_.empty());\n}\n\nTEST_F(ParserTest, PhonySelfReferenceKept) {\n  const char kInput[] =\n\"build a: phony a\\n\";\n  ManifestParserOptions parser_opts;\n  parser_opts.phony_cycle_action_ = kPhonyCycleActionError;\n  ManifestParser parser(&state, &fs_, parser_opts);\n  string err;\n  EXPECT_TRUE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"\", err);\n\n  Node* node = state.LookupNode(\"a\");\n  Edge* edge = node->in_edge();\n  ASSERT_EQ(edge->inputs_.size(), size_t(1));\n  ASSERT_EQ(edge->inputs_[0], node);\n}\n\nTEST_F(ParserTest, ReservedWords) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule build\\n\"\n\"  command = rule run $out\\n\"\n\"build subninja: build include default foo.cc\\n\"\n\"default subninja\\n\"));\n}\n\nTEST_F(ParserTest, Errors) {\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(string(\"subn\", 4), &err));\n    EXPECT_EQ(\"input:1: expected '=', got eof\\n\"\n              \"subn\\n\"\n              \"    ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"foobar\", &err));\n    EXPECT_EQ(\"input:1: expected '=', got eof\\n\"\n              \"foobar\\n\"\n              \"      ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"x 3\", &err));\n    EXPECT_EQ(\"input:1: expected '=', got identifier\\n\"\n              \"x 3\\n\"\n              \"  ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"x = 3\", &err));\n    EXPECT_EQ(\"input:1: unexpected EOF\\n\"\n              \"x = 3\\n\"\n              \"     ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"x = 3\\ny 2\", &err));\n    EXPECT_EQ(\"input:2: expected '=', got identifier\\n\"\n              \"y 2\\n\"\n              \"  ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"x = $\", &err));\n    EXPECT_EQ(\"input:1: bad $-escape (literal $ must be written as $$)\\n\"\n              \"x = $\\n\"\n              \"    ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"x = $\\n $[\\n\", &err));\n    EXPECT_EQ(\"input:2: bad $-escape (literal $ must be written as $$)\\n\"\n              \" $[\\n\"\n              \" ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"x = a$\\n b$\\n $\\n\", &err));\n    EXPECT_EQ(\"input:4: unexpected EOF\\n\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"build\\n\", &err));\n    EXPECT_EQ(\"input:1: expected path\\n\"\n              \"build\\n\"\n              \"     ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"build x: y z\\n\", &err));\n    EXPECT_EQ(\"input:1: unknown build rule 'y'\\n\"\n              \"build x: y z\\n\"\n              \"         ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"build x:: y z\\n\", &err));\n    EXPECT_EQ(\"input:1: expected build command name\\n\"\n              \"build x:: y z\\n\"\n              \"        ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cat\\n  command = cat ok\\n\"\n                                  \"build x: cat $\\n :\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:4: expected newline, got ':'\\n\"\n              \" :\\n\"\n              \" ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cat\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:2: expected 'command =' line\\n\", err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cat\\n\"\n                                  \"  command = echo\\n\"\n                                  \"rule cat\\n\"\n                                  \"  command = echo\\n\", &err));\n    EXPECT_EQ(\"input:3: duplicate rule 'cat'\\n\"\n              \"rule cat\\n\"\n              \"        ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cat\\n\"\n                                  \"  command = echo\\n\"\n                                  \"  rspfile = cat.rsp\\n\", &err));\n    EXPECT_EQ(\n        \"input:4: rspfile and rspfile_content need to be both specified\\n\",\n        err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cat\\n\"\n                                  \"  command = ${fafsd\\n\"\n                                  \"foo = bar\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:2: bad $-escape (literal $ must be written as $$)\\n\"\n              \"  command = ${fafsd\\n\"\n              \"            ^ near here\"\n              , err);\n  }\n\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cat\\n\"\n                                  \"  command = cat\\n\"\n                                  \"build $.: cat foo\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:3: bad $-escape (literal $ must be written as $$)\\n\"\n              \"build $.: cat foo\\n\"\n              \"      ^ near here\"\n              , err);\n  }\n\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cat\\n\"\n                                  \"  command = cat\\n\"\n                                  \"build $: cat foo\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:3: expected ':', got newline ($ also escapes ':')\\n\"\n              \"build $: cat foo\\n\"\n              \"                ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule %foo\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:1: expected rule name\\n\"\n              \"rule %foo\\n\"\n              \"     ^ near here\",\n              err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cc\\n\"\n                                  \"  command = foo\\n\"\n                                  \"  othervar = bar\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:3: unexpected variable 'othervar'\\n\"\n              \"  othervar = bar\\n\"\n              \"                ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cc\\n  command = foo\\n\"\n                                  \"build $.: cc bar.cc\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:3: bad $-escape (literal $ must be written as $$)\\n\"\n              \"build $.: cc bar.cc\\n\"\n              \"      ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cc\\n  command = foo\\n  && bar\",\n                                  &err));\n    EXPECT_EQ(\"input:3: expected variable name\\n\"\n              \"  && bar\\n\"\n              \"  ^ near here\",\n              err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule cc\\n  command = foo\\n\"\n                                  \"build $: cc bar.cc\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:3: expected ':', got newline ($ also escapes ':')\\n\"\n              \"build $: cc bar.cc\\n\"\n              \"                  ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"default\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:1: expected target name\\n\"\n              \"default\\n\"\n              \"       ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"default nonexistent\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:1: unknown target 'nonexistent'\\n\"\n              \"default nonexistent\\n\"\n              \"                   ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule r\\n  command = r\\n\"\n                                  \"build b: r\\n\"\n                                  \"default b:\\n\",\n                                  &err));\n    EXPECT_EQ(\"input:4: expected newline, got ':'\\n\"\n              \"default b:\\n\"\n              \"         ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"default $a\\n\", &err));\n    EXPECT_EQ(\"input:1: empty path\\n\"\n              \"default $a\\n\"\n              \"          ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"rule r\\n\"\n                                  \"  command = r\\n\"\n                                  \"build $a: r $c\\n\", &err));\n    // XXX the line number is wrong; we should evaluate paths in ParseEdge\n    // as we see them, not after we've read them all!\n    EXPECT_EQ(\"input:4: empty path\\n\", err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    // the indented blank line must terminate the rule\n    // this also verifies that \"unexpected (token)\" errors are correct\n    EXPECT_FALSE(parser.ParseTest(\"rule r\\n\"\n                                  \"  command = r\\n\"\n                                  \"  \\n\"\n                                  \"  generator = 1\\n\", &err));\n    EXPECT_EQ(\"input:4: unexpected indent\\n\", err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"pool\\n\", &err));\n    EXPECT_EQ(\"input:1: expected pool name\\n\"\n              \"pool\\n\"\n              \"    ^ near here\", err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"pool foo\\n\", &err));\n    EXPECT_EQ(\"input:2: expected 'depth =' line\\n\", err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"pool foo\\n\"\n                                  \"  depth = 4\\n\"\n                                  \"pool foo\\n\", &err));\n    EXPECT_EQ(\"input:3: duplicate pool 'foo'\\n\"\n              \"pool foo\\n\"\n              \"        ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"pool foo\\n\"\n                                  \"  depth = -1\\n\", &err));\n    EXPECT_EQ(\"input:2: invalid pool depth\\n\"\n              \"  depth = -1\\n\"\n              \"            ^ near here\"\n              , err);\n  }\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"pool foo\\n\"\n                                  \"  depth = foo\\n\", &err));\n    EXPECT_EQ(\"input:2: invalid pool depth\\n\"\n              \"  depth = foo\\n\"\n              \"             ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    EXPECT_FALSE(parser.ParseTest(\"pool foo\\n\"\n                                  \"  bar = 1\\n\", &err));\n    EXPECT_EQ(\"input:2: unexpected variable 'bar'\\n\"\n              \"  bar = 1\\n\"\n              \"         ^ near here\"\n              , err);\n  }\n\n  {\n    State local_state;\n    ManifestParser parser(&local_state, NULL);\n    string err;\n    // Pool names are dereferenced at edge parsing time.\n    EXPECT_FALSE(parser.ParseTest(\"rule run\\n\"\n                                  \"  command = echo\\n\"\n                                  \"  pool = unnamed_pool\\n\"\n                                  \"build out: run in\\n\", &err));\n    EXPECT_EQ(\"input:5: unknown pool name 'unnamed_pool'\\n\", err);\n  }\n}\n\nTEST_F(ParserTest, MissingInput) {\n  State local_state;\n  ManifestParser parser(&local_state, &fs_);\n  string err;\n  EXPECT_FALSE(parser.Load(\"build.ninja\", &err));\n  EXPECT_EQ(\"loading 'build.ninja': No such file or directory\", err);\n}\n\nTEST_F(ParserTest, MultipleOutputs) {\n  State local_state;\n  ManifestParser parser(&local_state, NULL);\n  string err;\n  EXPECT_TRUE(parser.ParseTest(\"rule cc\\n  command = foo\\n  depfile = bar\\n\"\n                               \"build a.o b.o: cc c.cc\\n\",\n                               &err));\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(ParserTest, MultipleOutputsWithDeps) {\n  State local_state;\n  ManifestParser parser(&local_state, NULL);\n  string err;\n  EXPECT_TRUE(parser.ParseTest(\"rule cc\\n  command = foo\\n  deps = gcc\\n\"\n                               \"build a.o b.o: cc c.cc\\n\",\n                               &err));\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(ParserTest, SubNinja) {\n  fs_.Create(\"test.ninja\",\n    \"var = inner\\n\"\n    \"build $builddir/inner: varref\\n\");\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"builddir = some_dir/\\n\"\n\"rule varref\\n\"\n\"  command = varref $var\\n\"\n\"var = outer\\n\"\n\"build $builddir/outer: varref\\n\"\n\"subninja test.ninja\\n\"\n\"build $builddir/outer2: varref\\n\"));\n  ASSERT_EQ(1u, fs_.files_read_.size());\n\n  EXPECT_EQ(\"test.ninja\", fs_.files_read_[0]);\n  EXPECT_TRUE(state.LookupNode(\"some_dir/outer\"));\n  // Verify our builddir setting is inherited.\n  EXPECT_TRUE(state.LookupNode(\"some_dir/inner\"));\n\n  ASSERT_EQ(3u, state.edges_.size());\n  EXPECT_EQ(\"varref outer\", state.edges_[0]->EvaluateCommand());\n  EXPECT_EQ(\"varref inner\", state.edges_[1]->EvaluateCommand());\n  EXPECT_EQ(\"varref outer\", state.edges_[2]->EvaluateCommand());\n}\n\nTEST_F(ParserTest, MissingSubNinja) {\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(\"subninja foo.ninja\\n\", &err));\n  EXPECT_EQ(\"input:1: loading 'foo.ninja': No such file or directory\\n\"\n            \"subninja foo.ninja\\n\"\n            \"                  ^ near here\"\n            , err);\n}\n\nTEST_F(ParserTest, DuplicateRuleInDifferentSubninjas) {\n  // Test that rules are scoped to subninjas.\n  fs_.Create(\"test.ninja\", \"rule cat\\n\"\n                         \"  command = cat\\n\");\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_TRUE(parser.ParseTest(\"rule cat\\n\"\n                                \"  command = cat\\n\"\n                                \"subninja test.ninja\\n\", &err));\n}\n\nTEST_F(ParserTest, DuplicateRuleInDifferentSubninjasWithInclude) {\n  // Test that rules are scoped to subninjas even with includes.\n  fs_.Create(\"rules.ninja\", \"rule cat\\n\"\n                         \"  command = cat\\n\");\n  fs_.Create(\"test.ninja\", \"include rules.ninja\\n\"\n                         \"build x : cat\\n\");\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_TRUE(parser.ParseTest(\"include rules.ninja\\n\"\n                                \"subninja test.ninja\\n\"\n                                \"build y : cat\\n\", &err));\n}\n\nTEST_F(ParserTest, Include) {\n  fs_.Create(\"include.ninja\", \"var = inner\\n\");\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"var = outer\\n\"\n\"include include.ninja\\n\"));\n\n  ASSERT_EQ(1u, fs_.files_read_.size());\n  EXPECT_EQ(\"include.ninja\", fs_.files_read_[0]);\n  EXPECT_EQ(\"inner\", state.bindings_.LookupVariable(\"var\"));\n}\n\nTEST_F(ParserTest, BrokenInclude) {\n  fs_.Create(\"include.ninja\", \"build\\n\");\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(\"include include.ninja\\n\", &err));\n  EXPECT_EQ(\"include.ninja:1: expected path\\n\"\n            \"build\\n\"\n            \"     ^ near here\"\n            , err);\n}\n\nTEST_F(ParserTest, Implicit) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build foo: cat bar | baz\\n\"));\n\n  Edge* edge = state.LookupNode(\"foo\")->in_edge();\n  ASSERT_TRUE(edge->is_implicit(1));\n}\n\nTEST_F(ParserTest, OrderOnly) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n  command = cat $in > $out\\n\"\n\"build foo: cat bar || baz\\n\"));\n\n  Edge* edge = state.LookupNode(\"foo\")->in_edge();\n  ASSERT_TRUE(edge->is_order_only(1));\n}\n\nTEST_F(ParserTest, Validations) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n  command = cat $in > $out\\n\"\n\"build foo: cat bar |@ baz\\n\"));\n\n  Edge* edge = state.LookupNode(\"foo\")->in_edge();\n  ASSERT_EQ(edge->validations_.size(), size_t(1));\n  EXPECT_EQ(edge->validations_[0]->path(), \"baz\");\n}\n\nTEST_F(ParserTest, ImplicitOutput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build foo | imp: cat bar\\n\"));\n\n  Edge* edge = state.LookupNode(\"imp\")->in_edge();\n  ASSERT_EQ(edge->outputs_.size(), size_t(2));\n  EXPECT_TRUE(edge->is_implicit_out(1));\n}\n\nTEST_F(ParserTest, ImplicitOutputEmpty) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build foo | : cat bar\\n\"));\n\n  Edge* edge = state.LookupNode(\"foo\")->in_edge();\n  ASSERT_EQ(edge->outputs_.size(), size_t(1));\n  EXPECT_FALSE(edge->is_implicit_out(0));\n}\n\nTEST_F(ParserTest, ImplicitOutputDupeError) {\n  const char kInput[] =\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build foo baz | foo baq foo: cat bar\\n\";\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:4: foo is defined as an output multiple times\\n\", err);\n}\n\nTEST_F(ParserTest, ImplicitOutputDupesError) {\n  const char kInput[] =\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build foo foo foo | foo foo foo foo: cat bar\\n\";\n  ManifestParser parser(&state, &fs_);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(kInput, &err));\n  EXPECT_EQ(\"input:4: foo is defined as an output multiple times\\n\", err);\n}\n\nTEST_F(ParserTest, NoExplicitOutput) {\n  ManifestParser parser(&state, NULL);\n  string err;\n  EXPECT_TRUE(parser.ParseTest(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build | imp : cat bar\\n\", &err));\n}\n\nTEST_F(ParserTest, DefaultDefault) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n  command = cat $in > $out\\n\"\n\"build a: cat foo\\n\"\n\"build b: cat foo\\n\"\n\"build c: cat foo\\n\"\n\"build d: cat foo\\n\"));\n\n  string err;\n  EXPECT_EQ(4u, state.DefaultNodes(&err).size());\n  EXPECT_EQ(\"\", err);\n}\n\nTEST_F(ParserTest, DefaultDefaultCycle) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n  command = cat $in > $out\\n\"\n\"build a: cat a\\n\"));\n\n  string err;\n  EXPECT_EQ(0u, state.DefaultNodes(&err).size());\n  EXPECT_EQ(\"could not determine root nodes of build graph\", err);\n}\n\nTEST_F(ParserTest, DefaultStatements) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n  command = cat $in > $out\\n\"\n\"build a: cat foo\\n\"\n\"build b: cat foo\\n\"\n\"build c: cat foo\\n\"\n\"build d: cat foo\\n\"\n\"third = c\\n\"\n\"default a b\\n\"\n\"default $third\\n\"));\n\n  string err;\n  vector<Node*> nodes = state.DefaultNodes(&err);\n  EXPECT_EQ(\"\", err);\n  ASSERT_EQ(3u, nodes.size());\n  EXPECT_EQ(\"a\", nodes[0]->path());\n  EXPECT_EQ(\"b\", nodes[1]->path());\n  EXPECT_EQ(\"c\", nodes[2]->path());\n}\n\nTEST_F(ParserTest, UTF8) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule utf8\\n\"\n\"  command = true\\n\"\n\"  description = compilaci\\xC3\\xB3\\n\"));\n}\n\nTEST_F(ParserTest, CRLF) {\n  State local_state;\n  ManifestParser parser(&local_state, NULL);\n  string err;\n\n  EXPECT_TRUE(parser.ParseTest(\"# comment with crlf\\r\\n\", &err));\n  EXPECT_TRUE(parser.ParseTest(\"foo = foo\\nbar = bar\\r\\n\", &err));\n  EXPECT_TRUE(parser.ParseTest(\n      \"pool link_pool\\r\\n\"\n      \"  depth = 15\\r\\n\\r\\n\"\n      \"rule xyz\\r\\n\"\n      \"  command = something$expand \\r\\n\"\n      \"  description = YAY!\\r\\n\",\n      &err));\n}\n\nTEST_F(ParserTest, DyndepNotSpecified) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build result: cat in\\n\"));\n  Edge* edge = state.GetNode(\"result\", 0)->in_edge();\n  ASSERT_FALSE(edge->dyndep_);\n}\n\nTEST_F(ParserTest, DyndepNotInput) {\n  State lstate;\n  ManifestParser parser(&lstate, NULL);\n  string err;\n  EXPECT_FALSE(parser.ParseTest(\n\"rule touch\\n\"\n\"  command = touch $out\\n\"\n\"build result: touch\\n\"\n\"  dyndep = notin\\n\",\n                               &err));\n  EXPECT_EQ(\"input:5: dyndep 'notin' is not an input\\n\", err);\n}\n\nTEST_F(ParserTest, DyndepExplicitInput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build result: cat in\\n\"\n\"  dyndep = in\\n\"));\n  Edge* edge = state.GetNode(\"result\", 0)->in_edge();\n  ASSERT_TRUE(edge->dyndep_);\n  EXPECT_TRUE(edge->dyndep_->dyndep_pending());\n  EXPECT_EQ(edge->dyndep_->path(), \"in\");\n}\n\nTEST_F(ParserTest, DyndepImplicitInput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build result: cat in | dd\\n\"\n\"  dyndep = dd\\n\"));\n  Edge* edge = state.GetNode(\"result\", 0)->in_edge();\n  ASSERT_TRUE(edge->dyndep_);\n  EXPECT_TRUE(edge->dyndep_->dyndep_pending());\n  EXPECT_EQ(edge->dyndep_->path(), \"dd\");\n}\n\nTEST_F(ParserTest, DyndepOrderOnlyInput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"build result: cat in || dd\\n\"\n\"  dyndep = dd\\n\"));\n  Edge* edge = state.GetNode(\"result\", 0)->in_edge();\n  ASSERT_TRUE(edge->dyndep_);\n  EXPECT_TRUE(edge->dyndep_->dyndep_pending());\n  EXPECT_EQ(edge->dyndep_->path(), \"dd\");\n}\n\nTEST_F(ParserTest, DyndepRuleInput) {\n  ASSERT_NO_FATAL_FAILURE(AssertParse(\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\"\n\"  dyndep = $in\\n\"\n\"build result: cat in\\n\"));\n  Edge* edge = state.GetNode(\"result\", 0)->in_edge();\n  ASSERT_TRUE(edge->dyndep_);\n  EXPECT_TRUE(edge->dyndep_->dyndep_pending());\n  EXPECT_EQ(edge->dyndep_->path(), \"in\");\n}\n"
  },
  {
    "path": "src/metrics.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"metrics.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n\n#include <algorithm>\n#include <chrono>\n\n#include \"util.h\"\n\nusing namespace std;\n\nMetrics* g_metrics = NULL;\n\nnamespace {\n\n/// Compute a platform-specific high-res timer value that fits into an int64.\nint64_t HighResTimer() {\n  auto now = chrono::steady_clock::now();\n  return chrono::duration_cast<chrono::steady_clock::duration>(\n             now.time_since_epoch())\n      .count();\n}\n\nint64_t TimerToMicros(int64_t dt) {\n  // dt is in ticks.  We want microseconds.\n  return chrono::duration_cast<chrono::microseconds>(\n             std::chrono::steady_clock::duration{ dt })\n      .count();\n}\n\nint64_t TimerToMicros(double dt) {\n  // dt is in ticks.  We want microseconds.\n  using DoubleSteadyClock =\n      std::chrono::duration<double, std::chrono::steady_clock::period>;\n  return chrono::duration_cast<chrono::microseconds>(DoubleSteadyClock{ dt })\n      .count();\n}\n\n}  // anonymous namespace\n\nScopedMetric::ScopedMetric(Metric* metric) {\n  metric_ = metric;\n  if (!metric_)\n    return;\n  start_ = HighResTimer();\n}\nScopedMetric::~ScopedMetric() {\n  if (!metric_)\n    return;\n  metric_->count++;\n  // Leave in the timer's natural frequency to avoid paying the conversion cost\n  // on every measurement.\n  int64_t dt = HighResTimer() - start_;\n  metric_->sum += dt;\n}\n\nMetric* Metrics::NewMetric(const string& name) {\n  Metric* metric = new Metric;\n  metric->name = name;\n  metric->count = 0;\n  metric->sum = 0;\n  metrics_.push_back(metric);\n  return metric;\n}\n\nvoid Metrics::Report() {\n  int width = 0;\n  for (vector<Metric*>::iterator i = metrics_.begin();\n       i != metrics_.end(); ++i) {\n    width = max((int)(*i)->name.size(), width);\n  }\n\n  printf(\"%-*s\\t%-6s\\t%-9s\\t%s\\n\", width,\n         \"metric\", \"count\", \"avg (us)\", \"total (ms)\");\n  for (vector<Metric*>::iterator i = metrics_.begin();\n       i != metrics_.end(); ++i) {\n    Metric* metric = *i;\n    uint64_t micros = TimerToMicros(metric->sum);\n    double total = micros / (double)1000;\n    double avg = micros / (double)metric->count;\n    printf(\"%-*s\\t%-6d\\t%-8.1f\\t%.1f\\n\", width, metric->name.c_str(),\n           metric->count, avg, total);\n  }\n}\n\ndouble Stopwatch::Elapsed() const {\n  // Convert to micros after converting to double to minimize error.\n  return 1e-6 * TimerToMicros(static_cast<double>(NowRaw() - started_));\n}\n\nuint64_t Stopwatch::NowRaw() const {\n  return HighResTimer();\n}\n\nint64_t GetTimeMillis() {\n  return TimerToMicros(HighResTimer()) / 1000;\n}\n"
  },
  {
    "path": "src/metrics.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_METRICS_H_\n#define NINJA_METRICS_H_\n\n#include <string>\n#include <vector>\n\n#include \"util.h\"  // For int64_t.\n\n/// The Metrics module is used for the debug mode that dumps timing stats of\n/// various actions.  To use, see METRIC_RECORD below.\n\n/// A single metrics we're tracking, like \"depfile load time\".\nstruct Metric {\n  std::string name;\n  /// Number of times we've hit the code path.\n  int count;\n  /// Total time (in platform-dependent units) we've spent on the code path.\n  int64_t sum;\n};\n\n/// A scoped object for recording a metric across the body of a function.\n/// Used by the METRIC_RECORD macro.\nstruct ScopedMetric {\n  explicit ScopedMetric(Metric* metric);\n  ~ScopedMetric();\n\nprivate:\n  Metric* metric_;\n  /// Timestamp when the measurement started.\n  /// Value is platform-dependent.\n  int64_t start_;\n};\n\n/// The singleton that stores metrics and prints the report.\nstruct Metrics {\n  Metric* NewMetric(const std::string& name);\n\n  /// Print a summary report to stdout.\n  void Report();\n\nprivate:\n  std::vector<Metric*> metrics_;\n};\n\n/// Get the current time as relative to some epoch.\n/// Epoch varies between platforms; only useful for measuring elapsed time.\nint64_t GetTimeMillis();\n\n/// A simple stopwatch which returns the time\n/// in seconds since Restart() was called.\nstruct Stopwatch {\n public:\n  Stopwatch() : started_(0) {}\n\n  /// Seconds since Restart() call.\n  double Elapsed() const;\n\n  void Restart() { started_ = NowRaw(); }\n\n private:\n  uint64_t started_;\n  // Return the current time using the native frequency of the high resolution\n  // timer.\n  uint64_t NowRaw() const;\n};\n\n/// The primary interface to metrics.  Use METRIC_RECORD(\"foobar\") at the top\n/// of a function to get timing stats recorded for each call of the function.\n#define METRIC_RECORD(name)                                             \\\n  static Metric* metrics_h_metric =                                     \\\n      g_metrics ? g_metrics->NewMetric(name) : NULL;                    \\\n  ScopedMetric metrics_h_scoped(metrics_h_metric);\n\n/// A variant of METRIC_RECORD that doesn't record anything if |condition|\n/// is false.\n#define METRIC_RECORD_IF(name, condition)            \\\n  static Metric* metrics_h_metric =                  \\\n      g_metrics ? g_metrics->NewMetric(name) : NULL; \\\n  ScopedMetric metrics_h_scoped((condition) ? metrics_h_metric : NULL);\n\nextern Metrics* g_metrics;\n\n#endif // NINJA_METRICS_H_\n"
  },
  {
    "path": "src/minidump-win32.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifdef _MSC_VER\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include \"util.h\"\n\nusing namespace std;\n\ntypedef BOOL (WINAPI *MiniDumpWriteDumpFunc) (\n    IN HANDLE,\n    IN DWORD,\n    IN HANDLE,\n    IN MINIDUMP_TYPE,\n    IN CONST PMINIDUMP_EXCEPTION_INFORMATION, OPTIONAL\n    IN CONST PMINIDUMP_USER_STREAM_INFORMATION, OPTIONAL\n    IN CONST PMINIDUMP_CALLBACK_INFORMATION OPTIONAL\n    );\n\n/// Creates a windows minidump in temp folder.\nvoid CreateWin32MiniDump(_EXCEPTION_POINTERS* pep) {\n  char temp_path[MAX_PATH];\n  GetTempPathA(sizeof(temp_path), temp_path);\n  char temp_file[MAX_PATH];\n  sprintf(temp_file, \"%s\\\\ninja_crash_dump_%lu.dmp\",\n          temp_path, GetCurrentProcessId());\n\n  // Delete any previous minidump of the same name.\n  DeleteFileA(temp_file);\n\n  // Load DbgHelp.dll dynamically, as library is not present on all\n  // Windows versions.\n  HMODULE dbghelp = LoadLibraryA(\"dbghelp.dll\");\n  if (dbghelp == NULL) {\n    Error(\"failed to create minidump: LoadLibrary('dbghelp.dll'): %s\",\n          GetLastErrorString().c_str());\n    return;\n  }\n\n  MiniDumpWriteDumpFunc mini_dump_write_dump = FunctionCast\n      <MiniDumpWriteDumpFunc>(GetProcAddress(dbghelp, \"MiniDumpWriteDump\"));\n  if (mini_dump_write_dump == NULL) {\n    Error(\"failed to create minidump: GetProcAddress('MiniDumpWriteDump'): %s\",\n          GetLastErrorString().c_str());\n    return;\n  }\n\n  HANDLE hFile = CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL,\n                             CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n  if (hFile == NULL) {\n    Error(\"failed to create minidump: CreateFileA(%s): %s\",\n          temp_file, GetLastErrorString().c_str());\n    return;\n  }\n\n  MINIDUMP_EXCEPTION_INFORMATION mdei;\n  mdei.ThreadId           = GetCurrentThreadId();\n  mdei.ExceptionPointers  = pep;\n  mdei.ClientPointers     = FALSE;\n  MINIDUMP_TYPE mdt       = (MINIDUMP_TYPE) (MiniDumpWithDataSegs |\n                                             MiniDumpWithHandleData);\n\n  BOOL rv = mini_dump_write_dump(GetCurrentProcess(), GetCurrentProcessId(),\n                                 hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0);\n  CloseHandle(hFile);\n\n  if (!rv) {\n    Error(\"MiniDumpWriteDump failed: %s\", GetLastErrorString().c_str());\n    return;\n  }\n\n  Warning(\"minidump created: %s\", temp_file);\n}\n\n#endif  // _MSC_VER\n"
  },
  {
    "path": "src/missing_deps.cc",
    "content": "// Copyright 2019 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"missing_deps.h\"\n\n#include <string.h>\n\n#include <iostream>\n\n#include \"depfile_parser.h\"\n#include \"deps_log.h\"\n#include \"disk_interface.h\"\n#include \"graph.h\"\n#include \"state.h\"\n#include \"util.h\"\n\nnamespace {\n\n/// ImplicitDepLoader variant that stores dep nodes into the given output\n/// without updating graph deps like the base loader does.\nstruct NodeStoringImplicitDepLoader : public ImplicitDepLoader {\n  NodeStoringImplicitDepLoader(\n      State* state, DepsLog* deps_log, DiskInterface* disk_interface,\n      DepfileParserOptions const* depfile_parser_options,\n      Explanations* explanations, std::vector<Node*>* dep_nodes_output)\n      : ImplicitDepLoader(state, deps_log, disk_interface,\n                          depfile_parser_options, explanations),\n        dep_nodes_output_(dep_nodes_output) {}\n\n protected:\n  virtual bool ProcessDepfileDeps(Edge* edge,\n                                  std::vector<StringPiece>* depfile_ins,\n                                  std::string* err);\n\n private:\n  std::vector<Node*>* dep_nodes_output_;\n};\n\nbool NodeStoringImplicitDepLoader::ProcessDepfileDeps(\n    Edge* edge, std::vector<StringPiece>* depfile_ins, std::string* err) {\n  for (std::vector<StringPiece>::iterator i = depfile_ins->begin();\n       i != depfile_ins->end(); ++i) {\n    uint64_t slash_bits;\n    CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits);\n    Node* node = state_->GetNode(*i, slash_bits);\n    dep_nodes_output_->push_back(node);\n  }\n  return true;\n}\n\n}  // namespace\n\nMissingDependencyScannerDelegate::~MissingDependencyScannerDelegate() {}\n\nvoid MissingDependencyPrinter::OnMissingDep(Node* node, const std::string& path,\n                                            const Rule& generator) {\n  std::cout << \"Missing dep: \" << node->path() << \" uses \" << path\n            << \" (generated by \" << generator.name() << \")\\n\";\n}\n\nMissingDependencyScanner::MissingDependencyScanner(\n    MissingDependencyScannerDelegate* delegate, DepsLog* deps_log, State* state,\n    DiskInterface* disk_interface)\n    : delegate_(delegate), deps_log_(deps_log), state_(state),\n      disk_interface_(disk_interface), missing_dep_path_count_(0) {}\n\nvoid MissingDependencyScanner::ProcessNode(Node* node) {\n  if (!node)\n    return;\n  Edge* edge = node->in_edge();\n  if (!edge)\n    return;\n  if (!seen_.insert(node).second)\n    return;\n\n  for (std::vector<Node*>::iterator in = edge->inputs_.begin();\n       in != edge->inputs_.end(); ++in) {\n    ProcessNode(*in);\n  }\n\n  std::string deps_type = edge->GetBinding(\"deps\");\n  if (!deps_type.empty()) {\n    DepsLog::Deps* deps = deps_log_->GetDeps(node);\n    if (deps)\n      ProcessNodeDeps(node, deps->nodes, deps->node_count);\n  } else {\n    DepfileParserOptions parser_opts;\n    std::vector<Node*> depfile_deps;\n    NodeStoringImplicitDepLoader dep_loader(state_, deps_log_, disk_interface_,\n                                            &parser_opts, nullptr,\n                                            &depfile_deps);\n    std::string err;\n    dep_loader.LoadDeps(edge, &err);\n    if (!depfile_deps.empty())\n      ProcessNodeDeps(node, &depfile_deps[0],\n                      static_cast<int>(depfile_deps.size()));\n  }\n}\n\nvoid MissingDependencyScanner::ProcessNodeDeps(Node* node, Node** dep_nodes,\n                                               int dep_nodes_count) {\n  Edge* edge = node->in_edge();\n  std::set<Edge*> deplog_edges;\n  for (int i = 0; i < dep_nodes_count; ++i) {\n    Node* deplog_node = dep_nodes[i];\n    // Special exception: A dep on build.ninja can be used to mean \"always\n    // rebuild this target when the build is reconfigured\", but build.ninja is\n    // often generated by a configuration tool like cmake or gn. The rest of\n    // the build \"implicitly\" depends on the entire build being reconfigured,\n    // so a missing dep path to build.ninja is not an actual missing dependency\n    // problem.\n    if (deplog_node->path() == \"build.ninja\")\n      return;\n    Edge* deplog_edge = deplog_node->in_edge();\n    if (deplog_edge) {\n      deplog_edges.insert(deplog_edge);\n    }\n  }\n  std::vector<Edge*> missing_deps;\n  for (std::set<Edge*>::iterator de = deplog_edges.begin();\n       de != deplog_edges.end(); ++de) {\n    if (!PathExistsBetween(*de, edge)) {\n      missing_deps.push_back(*de);\n    }\n  }\n\n  if (!missing_deps.empty()) {\n    std::set<std::string> missing_deps_rule_names;\n    for (std::vector<Edge*>::iterator ne = missing_deps.begin();\n         ne != missing_deps.end(); ++ne) {\n      for (int i = 0; i < dep_nodes_count; ++i) {\n        if (dep_nodes[i]->in_edge() == *ne) {\n          generated_nodes_.insert(dep_nodes[i]);\n          generator_rules_.insert(&(*ne)->rule());\n          missing_deps_rule_names.insert((*ne)->rule().name());\n          delegate_->OnMissingDep(node, dep_nodes[i]->path(), (*ne)->rule());\n        }\n      }\n    }\n    missing_dep_path_count_ += missing_deps_rule_names.size();\n    nodes_missing_deps_.insert(node);\n  }\n}\n\nvoid MissingDependencyScanner::PrintStats() {\n  std::cout << \"Processed \" << seen_.size() << \" nodes.\\n\";\n  if (HadMissingDeps()) {\n    std::cout << \"Error: There are \" << missing_dep_path_count_\n              << \" missing dependency paths.\\n\";\n    std::cout << nodes_missing_deps_.size()\n              << \" targets had depfile dependencies on \"\n              << generated_nodes_.size() << \" distinct generated inputs \"\n              << \"(from \" << generator_rules_.size() << \" rules) \"\n              << \" without a non-depfile dep path to the generator.\\n\";\n    std::cout << \"There might be build flakiness if any of the targets listed \"\n                 \"above are built alone, or not late enough, in a clean output \"\n                 \"directory.\\n\";\n  } else {\n    std::cout << \"No missing dependencies on generated files found.\\n\";\n  }\n}\n\nbool MissingDependencyScanner::PathExistsBetween(Edge* from, Edge* to) {\n  AdjacencyMap::iterator it = adjacency_map_.find(from);\n  if (it != adjacency_map_.end()) {\n    InnerAdjacencyMap::iterator inner_it = it->second.find(to);\n    if (inner_it != it->second.end()) {\n      return inner_it->second;\n    }\n  } else {\n    it = adjacency_map_.insert(std::make_pair(from, InnerAdjacencyMap())).first;\n  }\n  bool found = false;\n  for (size_t i = 0; i < to->inputs_.size(); ++i) {\n    Edge* e = to->inputs_[i]->in_edge();\n    if (e && (e == from || PathExistsBetween(from, e))) {\n      found = true;\n      break;\n    }\n  }\n  it->second.insert(std::make_pair(to, found));\n  return found;\n}\n"
  },
  {
    "path": "src/missing_deps.h",
    "content": "// Copyright 2019 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_MISSING_DEPS_H_\n#define NINJA_MISSING_DEPS_H_\n\n#include <map>\n#include <set>\n#include <string>\n\n#include <unordered_map>\n\nstruct DepsLog;\nstruct DiskInterface;\nstruct Edge;\nstruct Node;\nstruct Rule;\nstruct State;\n\nclass MissingDependencyScannerDelegate {\n public:\n  virtual ~MissingDependencyScannerDelegate();\n  virtual void OnMissingDep(Node* node, const std::string& path,\n                            const Rule& generator) = 0;\n};\n\nclass MissingDependencyPrinter : public MissingDependencyScannerDelegate {\n  void OnMissingDep(Node* node, const std::string& path, const Rule& generator);\n  void OnStats(int nodes_processed, int nodes_missing_deps,\n               int missing_dep_path_count, int generated_nodes,\n               int generator_rules);\n};\n\nstruct MissingDependencyScanner {\n public:\n  MissingDependencyScanner(MissingDependencyScannerDelegate* delegate,\n                           DepsLog* deps_log, State* state,\n                           DiskInterface* disk_interface);\n  void ProcessNode(Node* node);\n  void PrintStats();\n  bool HadMissingDeps() { return !nodes_missing_deps_.empty(); }\n\n  void ProcessNodeDeps(Node* node, Node** dep_nodes, int dep_nodes_count);\n\n  bool PathExistsBetween(Edge* from, Edge* to);\n\n  MissingDependencyScannerDelegate* delegate_;\n  DepsLog* deps_log_;\n  State* state_;\n  DiskInterface* disk_interface_;\n  std::set<Node*> seen_;\n  std::set<Node*> nodes_missing_deps_;\n  std::set<Node*> generated_nodes_;\n  std::set<const Rule*> generator_rules_;\n  int missing_dep_path_count_;\n\n private:\n  using InnerAdjacencyMap = std::unordered_map<Edge*, bool>;\n  using AdjacencyMap = std::unordered_map<Edge*, InnerAdjacencyMap>;\n  AdjacencyMap adjacency_map_;\n};\n\n#endif  // NINJA_MISSING_DEPS_H_\n"
  },
  {
    "path": "src/missing_deps_test.cc",
    "content": "// Copyright 2019 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <memory>\n\n#include \"deps_log.h\"\n#include \"graph.h\"\n#include \"missing_deps.h\"\n#include \"state.h\"\n#include \"test.h\"\n\nconst char kTestDepsLogFilename[] = \"MissingDepTest-tempdepslog\";\n\nclass MissingDependencyTestDelegate : public MissingDependencyScannerDelegate {\n  void OnMissingDep(Node* node, const std::string& path,\n                    const Rule& generator) {}\n};\n\nstruct MissingDependencyScannerTest : public testing::Test {\n  MissingDependencyScannerTest()\n      : generator_rule_(\"generator_rule\"), compile_rule_(\"compile_rule\"),\n        scanner_(&delegate_, &deps_log_, &state_, &filesystem_) {\n    std::string err;\n    deps_log_.OpenForWrite(kTestDepsLogFilename, &err);\n    EXPECT_EQ(\"\", err);\n  }\n\n  ~MissingDependencyScannerTest() {\n    // Remove test file.\n    deps_log_.Close();\n  }\n\n  MissingDependencyScanner& scanner() { return scanner_; }\n\n  void RecordDepsLogDep(const std::string& from, const std::string& to) {\n    Node* node_deps[] = { state_.LookupNode(to) };\n    deps_log_.RecordDeps(state_.LookupNode(from), 0, 1, node_deps);\n  }\n\n  void ProcessAllNodes() {\n    std::string err;\n    std::vector<Node*> nodes = state_.RootNodes(&err);\n    EXPECT_EQ(\"\", err);\n    for (std::vector<Node*>::iterator it = nodes.begin(); it != nodes.end();\n         ++it) {\n      scanner().ProcessNode(*it);\n    }\n  }\n\n  void CreateInitialState() {\n    EvalString deps_type;\n    deps_type.AddText(\"gcc\");\n    compile_rule_.AddBinding(\"deps\", deps_type);\n    generator_rule_.AddBinding(\"deps\", deps_type);\n    Edge* header_edge = state_.AddEdge(&generator_rule_);\n    state_.AddOut(header_edge, \"generated_header\", 0, nullptr);\n    Edge* compile_edge = state_.AddEdge(&compile_rule_);\n    state_.AddOut(compile_edge, \"compiled_object\", 0, nullptr);\n  }\n\n  void CreateGraphDependencyBetween(const char* from, const char* to) {\n    Node* from_node = state_.LookupNode(from);\n    Edge* from_edge = from_node->in_edge();\n    state_.AddIn(from_edge, to, 0);\n  }\n\n  void AssertMissingDependencyBetween(const char* flaky, const char* generated,\n                                      Rule* rule) {\n    Node* flaky_node = state_.LookupNode(flaky);\n    ASSERT_EQ(1u, scanner().nodes_missing_deps_.count(flaky_node));\n    Node* generated_node = state_.LookupNode(generated);\n    ASSERT_EQ(1u, scanner().generated_nodes_.count(generated_node));\n    ASSERT_EQ(1u, scanner().generator_rules_.count(rule));\n  }\n\n  ScopedFilePath scoped_file_path_ = kTestDepsLogFilename;\n  MissingDependencyTestDelegate delegate_;\n  Rule generator_rule_;\n  Rule compile_rule_;\n  DepsLog deps_log_;\n  State state_;\n  VirtualFileSystem filesystem_;\n  MissingDependencyScanner scanner_;\n};\n\nTEST_F(MissingDependencyScannerTest, EmptyGraph) {\n  ProcessAllNodes();\n  ASSERT_FALSE(scanner().HadMissingDeps());\n}\n\nTEST_F(MissingDependencyScannerTest, NoMissingDep) {\n  CreateInitialState();\n  ProcessAllNodes();\n  ASSERT_FALSE(scanner().HadMissingDeps());\n}\n\nTEST_F(MissingDependencyScannerTest, MissingDepPresent) {\n  CreateInitialState();\n  // compiled_object uses generated_header, without a proper dependency\n  RecordDepsLogDep(\"compiled_object\", \"generated_header\");\n  ProcessAllNodes();\n  ASSERT_TRUE(scanner().HadMissingDeps());\n  ASSERT_EQ(1u, scanner().nodes_missing_deps_.size());\n  ASSERT_EQ(1u, scanner().missing_dep_path_count_);\n  AssertMissingDependencyBetween(\"compiled_object\", \"generated_header\",\n                                 &generator_rule_);\n}\n\nTEST_F(MissingDependencyScannerTest, MissingDepFixedDirect) {\n  CreateInitialState();\n  // Adding the direct dependency fixes the missing dep\n  CreateGraphDependencyBetween(\"compiled_object\", \"generated_header\");\n  RecordDepsLogDep(\"compiled_object\", \"generated_header\");\n  ProcessAllNodes();\n  ASSERT_FALSE(scanner().HadMissingDeps());\n}\n\nTEST_F(MissingDependencyScannerTest, MissingDepFixedIndirect) {\n  CreateInitialState();\n  // Adding an indirect dependency also fixes the issue\n  Edge* intermediate_edge = state_.AddEdge(&generator_rule_);\n  state_.AddOut(intermediate_edge, \"intermediate\", 0, nullptr);\n  CreateGraphDependencyBetween(\"compiled_object\", \"intermediate\");\n  CreateGraphDependencyBetween(\"intermediate\", \"generated_header\");\n  RecordDepsLogDep(\"compiled_object\", \"generated_header\");\n  ProcessAllNodes();\n  ASSERT_FALSE(scanner().HadMissingDeps());\n}\n\nTEST_F(MissingDependencyScannerTest, CyclicMissingDep) {\n  CreateInitialState();\n  RecordDepsLogDep(\"generated_header\", \"compiled_object\");\n  RecordDepsLogDep(\"compiled_object\", \"generated_header\");\n  // In case of a cycle, both paths are reported (and there is\n  // no way to fix the issue by adding deps).\n  ProcessAllNodes();\n  ASSERT_TRUE(scanner().HadMissingDeps());\n  ASSERT_EQ(2u, scanner().nodes_missing_deps_.size());\n  ASSERT_EQ(2u, scanner().missing_dep_path_count_);\n  AssertMissingDependencyBetween(\"compiled_object\", \"generated_header\",\n                                 &generator_rule_);\n  AssertMissingDependencyBetween(\"generated_header\", \"compiled_object\",\n                                 &compile_rule_);\n}\n\nTEST_F(MissingDependencyScannerTest, CycleInGraph) {\n  CreateInitialState();\n  CreateGraphDependencyBetween(\"compiled_object\", \"generated_header\");\n  CreateGraphDependencyBetween(\"generated_header\", \"compiled_object\");\n  // The missing-deps tool doesn't deal with cycles in the graph, because\n  // there will be an error loading the graph before we get to the tool.\n  // This test is to illustrate that.\n  std::string err;\n  std::vector<Node*> nodes = state_.RootNodes(&err);\n  ASSERT_NE(\"\", err);\n}\n"
  },
  {
    "path": "src/msvc_helper-win32.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"msvc_helper.h\"\n\n#include <windows.h>\n\n#include \"util.h\"\n\nusing namespace std;\n\nnamespace {\n\nstring Replace(const string& input, const string& find, const string& replace) {\n  string result = input;\n  size_t start_pos = 0;\n  while ((start_pos = result.find(find, start_pos)) != string::npos) {\n    result.replace(start_pos, find.length(), replace);\n    start_pos += replace.length();\n  }\n  return result;\n}\n\n}  // anonymous namespace\n\nstring EscapeForDepfile(const string& path) {\n  // Depfiles don't escape single \\.\n  return Replace(path, \" \", \"\\\\ \");\n}\n\nint CLWrapper::Run(const string& command, string* output) {\n  SECURITY_ATTRIBUTES security_attributes = {};\n  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\n  security_attributes.bInheritHandle = TRUE;\n\n  // Must be inheritable so subprocesses can dup to children.\n  HANDLE nul =\n      CreateFileA(\"NUL\", GENERIC_READ,\n                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n                  &security_attributes, OPEN_EXISTING, 0, NULL);\n  if (nul == INVALID_HANDLE_VALUE)\n    Fatal(\"couldn't open nul\");\n\n  HANDLE stdout_read, stdout_write;\n  if (!CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0))\n    Win32Fatal(\"CreatePipe\");\n\n  if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0))\n    Win32Fatal(\"SetHandleInformation\");\n\n  PROCESS_INFORMATION process_info = {};\n  STARTUPINFOA startup_info = {};\n  startup_info.cb = sizeof(STARTUPINFOA);\n  startup_info.hStdInput = nul;\n  startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);\n  startup_info.hStdOutput = stdout_write;\n  startup_info.dwFlags |= STARTF_USESTDHANDLES;\n\n  if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,\n                      /* inherit handles */ TRUE, 0,\n                      env_block_, NULL,\n                      &startup_info, &process_info)) {\n    Win32Fatal(\"CreateProcess\");\n  }\n\n  if (!CloseHandle(nul) ||\n      !CloseHandle(stdout_write)) {\n    Win32Fatal(\"CloseHandle\");\n  }\n\n  // Read all output of the subprocess.\n  DWORD read_len = 1;\n  while (read_len) {\n    char buf[64 << 10];\n    read_len = 0;\n    if (!::ReadFile(stdout_read, buf, sizeof(buf), &read_len, NULL) &&\n        GetLastError() != ERROR_BROKEN_PIPE) {\n      Win32Fatal(\"ReadFile\");\n    }\n    output->append(buf, read_len);\n  }\n\n  // Wait for it to exit and grab its exit code.\n  if (WaitForSingleObject(process_info.hProcess, INFINITE) == WAIT_FAILED)\n    Win32Fatal(\"WaitForSingleObject\");\n  DWORD exit_code = 0;\n  if (!GetExitCodeProcess(process_info.hProcess, &exit_code))\n    Win32Fatal(\"GetExitCodeProcess\");\n\n  if (!CloseHandle(stdout_read) ||\n      !CloseHandle(process_info.hProcess) ||\n      !CloseHandle(process_info.hThread)) {\n    Win32Fatal(\"CloseHandle\");\n  }\n\n  return exit_code;\n}\n"
  },
  {
    "path": "src/msvc_helper.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef MSVC_HELPER_H_\n#define MSVC_HELPER_H_\n\n#include <string>\n\nstd::string EscapeForDepfile(const std::string& path);\n\n/// Wraps a synchronous execution of a CL subprocess.\nstruct CLWrapper {\n  CLWrapper() : env_block_(NULL) {}\n\n  /// Set the environment block (as suitable for CreateProcess) to be used\n  /// by Run().\n  void SetEnvBlock(void* env_block) { env_block_ = env_block; }\n\n  /// Start a process and gather its raw output.  Returns its exit code.\n  /// Crashes (calls Fatal()) on error.\n  int Run(const std::string& command, std::string* output);\n\n  void* env_block_;\n};\n\n#endif  // MSVC_HELPER_H_\n"
  },
  {
    "path": "src/msvc_helper_main-win32.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"msvc_helper.h\"\n\n#include <fcntl.h>\n#include <io.h>\n#include <stdio.h>\n#include <windows.h>\n\n#include \"clparser.h\"\n#include \"util.h\"\n\n#include \"getopt.h\"\n\nusing namespace std;\n\nnamespace {\n\nvoid Usage() {\n  printf(\n\"usage: ninja -t msvc [options] -- cl.exe /showIncludes /otherArgs\\n\"\n\"options:\\n\"\n\"  -e ENVFILE load environment block from ENVFILE as environment\\n\"\n\"  -o FILE    write output dependency information to FILE.d\\n\"\n\"  -p STRING  localized prefix of msvc's /showIncludes output\\n\"\n         );\n}\n\nvoid PushPathIntoEnvironment(const string& env_block) {\n  const char* as_str = env_block.c_str();\n  while (as_str[0]) {\n    if (_strnicmp(as_str, \"path=\", 5) == 0) {\n      _putenv(as_str);\n      return;\n    } else {\n      as_str = &as_str[strlen(as_str) + 1];\n    }\n  }\n}\n\nvoid WriteDepFileOrDie(const char* object_path, const CLParser& parse) {\n  string depfile_path = string(object_path) + \".d\";\n  FILE* depfile = fopen(depfile_path.c_str(), \"w\");\n  if (!depfile) {\n    platformAwareUnlink(object_path);\n    Fatal(\"opening %s: %s\", depfile_path.c_str(),\n          GetLastErrorString().c_str());\n  }\n  if (fprintf(depfile, \"%s: \", object_path) < 0) {\n    platformAwareUnlink(object_path);\n    fclose(depfile);\n    platformAwareUnlink(depfile_path.c_str());\n    Fatal(\"writing %s\", depfile_path.c_str());\n  }\n  const set<string>& headers = parse.includes_;\n  for (set<string>::const_iterator i = headers.begin();\n       i != headers.end(); ++i) {\n    if (fprintf(depfile, \"%s\\n\", EscapeForDepfile(*i).c_str()) < 0) {\n      platformAwareUnlink(object_path);\n      fclose(depfile);\n      platformAwareUnlink(depfile_path.c_str());\n      Fatal(\"writing %s\", depfile_path.c_str());\n    }\n  }\n  fclose(depfile);\n}\n\n}  // anonymous namespace\n\nint MSVCHelperMain(int argc, char** argv) {\n  const char* output_filename = NULL;\n  const char* envfile = NULL;\n\n  const option kLongOptions[] = {\n    { \"help\", no_argument, NULL, 'h' },\n    { NULL, 0, NULL, 0 }\n  };\n  int opt;\n  string deps_prefix;\n  while ((opt = getopt_long(argc, argv, \"e:o:p:h\", kLongOptions, NULL)) != -1) {\n    switch (opt) {\n      case 'e':\n        envfile = optarg;\n        break;\n      case 'o':\n        output_filename = optarg;\n        break;\n      case 'p':\n        deps_prefix = optarg;\n        break;\n      case 'h':\n      default:\n        Usage();\n        return 0;\n    }\n  }\n\n  string env;\n  if (envfile) {\n    string err;\n    if (ReadFile(envfile, &env, &err) != 0)\n      Fatal(\"couldn't open %s: %s\", envfile, err.c_str());\n    PushPathIntoEnvironment(env);\n  }\n\n  char* command = GetCommandLineA();\n  command = strstr(command, \" -- \");\n  if (!command) {\n    Fatal(\"expected command line to end with \\\" -- command args\\\"\");\n  }\n  command += 4;\n\n  CLWrapper cl;\n  if (!env.empty())\n    cl.SetEnvBlock((void*)env.data());\n  string output;\n  int exit_code = cl.Run(command, &output);\n\n  if (output_filename) {\n    CLParser parser;\n    string err;\n    if (!parser.Parse(output, deps_prefix, &output, &err))\n      Fatal(\"%s\\n\", err.c_str());\n    WriteDepFileOrDie(output_filename, parser);\n  }\n\n  if (output.empty())\n    return exit_code;\n\n  // CLWrapper's output already as \\r\\n line endings, make sure the C runtime\n  // doesn't expand this to \\r\\r\\n.\n  _setmode(_fileno(stdout), _O_BINARY);\n  // Avoid printf and C strings, since the actual output might contain null\n  // bytes like UTF-16 does (yuck).\n  fwrite(&output[0], 1, output.size(), stdout);\n\n  return exit_code;\n}\n"
  },
  {
    "path": "src/msvc_helper_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"msvc_helper.h\"\n\n#include \"test.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nTEST(EscapeForDepfileTest, SpacesInFilename) {\n  ASSERT_EQ(\"sub\\\\some\\\\ sdk\\\\foo.h\",\n            EscapeForDepfile(\"sub\\\\some sdk\\\\foo.h\"));\n}\n\nTEST(MSVCHelperTest, EnvBlock) {\n  char env_block[] = \"foo=bar\\0\";\n  CLWrapper cl;\n  cl.SetEnvBlock(env_block);\n  string output;\n  cl.Run(\"cmd /c \\\"echo foo is %foo%\", &output);\n  ASSERT_EQ(\"foo is bar\\r\\n\", output);\n}\n\nTEST(MSVCHelperTest, NoReadOfStderr) {\n  CLWrapper cl;\n  string output;\n  cl.Run(\"cmd /c \\\"echo to stdout&& echo to stderr 1>&2\", &output);\n  ASSERT_EQ(\"to stdout\\r\\n\", output);\n}\n"
  },
  {
    "path": "src/ninja.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <errno.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <algorithm>\n#include <cstdlib>\n#include <cstring>\n#include <string>\n\n#ifdef _WIN32\n#include \"getopt.h\"\n#include <direct.h>\n#include <windows.h>\n#elif defined(_AIX)\n#include \"getopt.h\"\n#include <unistd.h>\n#else\n#include <getopt.h>\n#include <unistd.h>\n#endif\n\n#include \"browse.h\"\n#include \"build.h\"\n#include \"build_log.h\"\n#include \"clean.h\"\n#include \"command_collector.h\"\n#include \"debug_flags.h\"\n#include \"deps_log.h\"\n#include \"disk_interface.h\"\n#include \"exit_status.h\"\n#include \"graph.h\"\n#include \"graphviz.h\"\n#include \"jobserver.h\"\n#include \"json.h\"\n#include \"manifest_parser.h\"\n#include \"metrics.h\"\n#include \"missing_deps.h\"\n#include \"state.h\"\n#include \"status.h\"\n#include \"util.h\"\n#include \"version.h\"\n\nusing namespace std;\n\n#ifdef _WIN32\n// Defined in msvc_helper_main-win32.cc.\nint MSVCHelperMain(int argc, char** argv);\n\n// Defined in minidump-win32.cc.\nvoid CreateWin32MiniDump(_EXCEPTION_POINTERS* pep);\n#endif\n\nnamespace {\n\nstruct Tool;\n\n/// Command-line options.\nstruct Options {\n  /// Build file to load.\n  const char* input_file;\n\n  /// Directory to change into before running.\n  const char* working_dir;\n\n  /// Tool to run rather than building.\n  const Tool* tool;\n\n  /// Whether phony cycles should warn or print an error.\n  bool phony_cycle_should_err;\n};\n\n/// The Ninja main() loads up a series of data structures; various tools need\n/// to poke into these, so store them as fields on an object.\nstruct NinjaMain : public BuildLogUser {\n  NinjaMain(const char* ninja_command, const BuildConfig& config) :\n      ninja_command_(ninja_command), config_(config),\n      start_time_millis_(GetTimeMillis()) {}\n\n  /// Command line used to run Ninja.\n  const char* ninja_command_;\n\n  /// Build configuration set from flags (e.g. parallelism).\n  const BuildConfig& config_;\n\n  /// Loaded state (rules, nodes).\n  State state_;\n\n  /// Functions for accessing the disk.\n  RealDiskInterface disk_interface_;\n\n  /// The build directory, used for storing the build log etc.\n  string build_dir_;\n\n  BuildLog build_log_;\n  DepsLog deps_log_;\n\n  /// The type of functions that are the entry points to tools (subcommands).\n  typedef int (NinjaMain::*ToolFunc)(const Options*, int, char**);\n\n  /// Get the Node for a given command-line path, handling features like\n  /// spell correction.\n  Node* CollectTarget(const char* cpath, string* err);\n\n  /// CollectTarget for all command-line arguments, filling in \\a targets.\n  bool CollectTargetsFromArgs(int argc, char* argv[],\n                              vector<Node*>* targets, string* err);\n\n  // The various subcommands, run via \"-t XXX\".\n  int ToolGraph(const Options* options, int argc, char* argv[]);\n  int ToolQuery(const Options* options, int argc, char* argv[]);\n  int ToolDeps(const Options* options, int argc, char* argv[]);\n  int ToolMissingDeps(const Options* options, int argc, char* argv[]);\n  int ToolBrowse(const Options* options, int argc, char* argv[]);\n  int ToolMSVC(const Options* options, int argc, char* argv[]);\n  int ToolTargets(const Options* options, int argc, char* argv[]);\n  int ToolCommands(const Options* options, int argc, char* argv[]);\n  int ToolInputs(const Options* options, int argc, char* argv[]);\n  int ToolMultiInputs(const Options* options, int argc, char* argv[]);\n  int ToolClean(const Options* options, int argc, char* argv[]);\n  int ToolCleanDead(const Options* options, int argc, char* argv[]);\n  int ToolCompilationDatabase(const Options* options, int argc, char* argv[]);\n  int ToolCompilationDatabaseForTargets(const Options* options, int argc,\n                                        char* argv[]);\n  int ToolRecompact(const Options* options, int argc, char* argv[]);\n  int ToolRestat(const Options* options, int argc, char* argv[]);\n  int ToolUrtle(const Options* options, int argc, char** argv);\n  int ToolRules(const Options* options, int argc, char* argv[]);\n  int ToolWinCodePage(const Options* options, int argc, char* argv[]);\n\n  /// Open the build log.\n  /// @return false on error.\n  bool OpenBuildLog(bool recompact_only = false);\n\n  /// Open the deps log: load it, then open for writing.\n  /// @return false on error.\n  bool OpenDepsLog(bool recompact_only = false);\n\n  /// Ensure the build directory exists, creating it if necessary.\n  /// @return false on error.\n  bool EnsureBuildDirExists();\n\n  /// Rebuild the manifest, if necessary.\n  /// Fills in \\a err on error.\n  /// @return true if the manifest was rebuilt.\n  bool RebuildManifest(const char* input_file, string* err, Status* status);\n\n  /// For each edge, lookup in build log how long it took last time,\n  /// and record that in the edge itself. It will be used for ETA prediction.\n  void ParsePreviousElapsedTimes();\n\n  /// Create a jobserver client if needed. Return a nullptr value if\n  /// not. Prints info and warnings to \\a status.\n  std::unique_ptr<Jobserver::Client> SetupJobserverClient(Status* status);\n\n  /// Build the targets listed on the command line.\n  /// @return an exit code.\n  ExitStatus RunBuild(int argc, char** argv, Status* status);\n\n  /// Dump the output requested by '-d stats'.\n  void DumpMetrics();\n\n  virtual bool IsPathDead(StringPiece s) const {\n    Node* n = state_.LookupNode(s);\n    if (n && n->in_edge())\n      return false;\n    // Just checking n isn't enough: If an old output is both in the build log\n    // and in the deps log, it will have a Node object in state_.  (It will also\n    // have an in edge if one of its inputs is another output that's in the deps\n    // log, but having a deps edge product an output that's input to another deps\n    // edge is rare, and the first recompaction will delete all old outputs from\n    // the deps log, and then a second recompaction will clear the build log,\n    // which seems good enough for this corner case.)\n    // Do keep entries around for files which still exist on disk, for\n    // generators that want to use this information.\n    string err;\n    TimeStamp mtime = disk_interface_.Stat(s.AsString(), &err);\n    if (mtime == -1)\n      Error(\"%s\", err.c_str());  // Log and ignore Stat() errors.\n    return mtime == 0;\n  }\n\n  int64_t start_time_millis_;\n};\n\n/// Subtools, accessible via \"-t foo\".\nstruct Tool {\n  /// Short name of the tool.\n  const char* name;\n\n  /// Description (shown in \"-t list\").\n  const char* desc;\n\n  /// When to run the tool.\n  enum {\n    /// Run after parsing the command-line flags and potentially changing\n    /// the current working directory (as early as possible).\n    RUN_AFTER_FLAGS,\n\n    /// Run after loading build.ninja.\n    RUN_AFTER_LOAD,\n\n    /// Run after loading the build/deps logs.\n    RUN_AFTER_LOGS,\n  } when;\n\n  /// Implementation of the tool.\n  NinjaMain::ToolFunc func;\n};\n\n/// Print usage information.\nvoid Usage(const BuildConfig& config) {\n  fprintf(stderr,\n\"usage: ninja [options] [targets...]\\n\"\n\"\\n\"\n\"if targets are unspecified, builds the 'default' target (see manual).\\n\"\n\"\\n\"\n\"options:\\n\"\n\"  --version      print ninja version (\\\"%s\\\")\\n\"\n\"  -v, --verbose  show all command lines while building\\n\"\n\"  --quiet        don't show progress status, just command output\\n\"\n\"\\n\"\n\"  -C DIR   change to DIR before doing anything else\\n\"\n\"  -f FILE  specify input build file [default=build.ninja]\\n\"\n\"\\n\"\n\"  -j N     run N jobs in parallel (0 means infinity) [default=%d on this system]\\n\"\n\"  -k N     keep going until N jobs fail (0 means infinity) [default=1]\\n\"\n\"  -l N     do not start new jobs if the load average is greater than N\\n\"\n\"  -n       dry run (don't run commands but act like they succeeded)\\n\"\n\"\\n\"\n\"  -d MODE  enable debugging (use '-d list' to list modes)\\n\"\n\"  -t TOOL  run a subtool (use '-t list' to list subtools)\\n\"\n\"    terminates toplevel options; further flags are passed to the tool\\n\"\n\"  -w FLAG  adjust warnings (use '-w list' to list warnings)\\n\",\n          kNinjaVersion, config.parallelism);\n}\n\n/// Choose a default value for the -j (parallelism) flag.\nint GuessParallelism() {\n  switch (int processors = GetProcessorCount()) {\n  case 0:\n  case 1:\n    return 2;\n  case 2:\n    return 3;\n  default:\n    return processors + 2;\n  }\n}\n\n/// Rebuild the build manifest, if necessary.\n/// Returns true if the manifest was rebuilt.\nbool NinjaMain::RebuildManifest(const char* input_file, string* err,\n                                Status* status) {\n  string path = input_file;\n  if (path.empty()) {\n    *err = \"empty path\";\n    return false;\n  }\n  uint64_t slash_bits;  // Unused because this path is only used for lookup.\n  CanonicalizePath(&path, &slash_bits);\n  Node* node = state_.LookupNode(path);\n  if (!node)\n    return false;\n\n  Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_,\n                  status, start_time_millis_);\n  if (!builder.AddTarget(node, err))\n    return false;\n\n  if (builder.AlreadyUpToDate())\n    return false;  // Not an error, but we didn't rebuild.\n\n  if (builder.Build(err) != ExitSuccess)\n    return false;\n\n  // The manifest was only rebuilt if it is now dirty (it may have been cleaned\n  // by a restat).\n  if (!node->dirty()) {\n    // Reset the state to prevent problems like\n    // https://github.com/ninja-build/ninja/issues/874\n    state_.Reset();\n    return false;\n  }\n\n  return true;\n}\n\nvoid NinjaMain::ParsePreviousElapsedTimes() {\n  for (Edge* edge : state_.edges_) {\n    for (Node* out : edge->outputs_) {\n      BuildLog::LogEntry* log_entry = build_log_.LookupByOutput(out->path());\n      if (!log_entry)\n        continue;  // Maybe we'll have log entry for next output of this edge?\n      edge->prev_elapsed_time_millis =\n          log_entry->end_time - log_entry->start_time;\n      break;  // Onto next edge.\n    }\n  }\n}\n\nNode* NinjaMain::CollectTarget(const char* cpath, string* err) {\n  string path = cpath;\n  if (path.empty()) {\n    *err = \"empty path\";\n    return NULL;\n  }\n  uint64_t slash_bits;\n  CanonicalizePath(&path, &slash_bits);\n\n  // Special syntax: \"foo.cc^\" means \"the first output of foo.cc\".\n  bool first_dependent = false;\n  if (!path.empty() && path[path.size() - 1] == '^') {\n    path.resize(path.size() - 1);\n    first_dependent = true;\n  }\n\n  Node* node = state_.LookupNode(path);\n  if (node) {\n    if (first_dependent) {\n      if (node->out_edges().empty()) {\n        Node* rev_deps = deps_log_.GetFirstReverseDepsNode(node);\n        if (!rev_deps) {\n          *err = \"'\" + path + \"' has no out edge\";\n          return NULL;\n        }\n        node = rev_deps;\n      } else {\n        Edge* edge = node->out_edges()[0];\n        if (edge->outputs_.empty()) {\n          edge->Dump();\n          Fatal(\"edge has no outputs\");\n        }\n        node = edge->outputs_[0];\n      }\n    }\n    return node;\n  } else {\n    *err =\n        \"unknown target '\" + Node::PathDecanonicalized(path, slash_bits) + \"'\";\n    if (path == \"clean\") {\n      *err += \", did you mean 'ninja -t clean'?\";\n    } else if (path == \"help\") {\n      *err += \", did you mean 'ninja -h'?\";\n    } else {\n      Node* suggestion = state_.SpellcheckNode(path);\n      if (suggestion) {\n        *err += \", did you mean '\" + suggestion->path() + \"'?\";\n      }\n    }\n    return NULL;\n  }\n}\n\nbool NinjaMain::CollectTargetsFromArgs(int argc, char* argv[],\n                                       vector<Node*>* targets, string* err) {\n  if (argc == 0) {\n    *targets = state_.DefaultNodes(err);\n    return err->empty();\n  }\n\n  for (int i = 0; i < argc; ++i) {\n    Node* node = CollectTarget(argv[i], err);\n    if (node == NULL)\n      return false;\n    targets->push_back(node);\n  }\n  return true;\n}\n\nint NinjaMain::ToolGraph(const Options* options, int argc, char* argv[]) {\n  vector<Node*> nodes;\n  string err;\n  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {\n    Error(\"%s\", err.c_str());\n    return 1;\n  }\n\n  GraphViz graph(&state_, &disk_interface_);\n  graph.Start();\n  for (vector<Node*>::const_iterator n = nodes.begin(); n != nodes.end(); ++n)\n    graph.AddTarget(*n);\n  graph.Finish();\n\n  return 0;\n}\n\nint NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) {\n  if (argc == 0) {\n    Error(\"expected a target to query\");\n    return 1;\n  }\n\n  DyndepLoader dyndep_loader(&state_, &disk_interface_);\n\n  for (int i = 0; i < argc; ++i) {\n    string err;\n    Node* node = CollectTarget(argv[i], &err);\n    if (!node) {\n      Error(\"%s\", err.c_str());\n      return 1;\n    }\n\n    printf(\"%s:\\n\", node->path().c_str());\n    if (Edge* edge = node->in_edge()) {\n      if (edge->dyndep_ && edge->dyndep_->dyndep_pending()) {\n        if (!dyndep_loader.LoadDyndeps(edge->dyndep_, &err)) {\n          Warning(\"%s\\n\", err.c_str());\n        }\n      }\n      printf(\"  input: %s\\n\", edge->rule_->name().c_str());\n      for (int in = 0; in < (int)edge->inputs_.size(); in++) {\n        const char* label = \"\";\n        if (edge->is_implicit(in))\n          label = \"| \";\n        else if (edge->is_order_only(in))\n          label = \"|| \";\n        printf(\"    %s%s\\n\", label, edge->inputs_[in]->path().c_str());\n      }\n      if (!edge->validations_.empty()) {\n        printf(\"  validations:\\n\");\n        for (std::vector<Node*>::iterator validation = edge->validations_.begin();\n             validation != edge->validations_.end(); ++validation) {\n          printf(\"    %s\\n\", (*validation)->path().c_str());\n        }\n      }\n    }\n    printf(\"  outputs:\\n\");\n    for (vector<Edge*>::const_iterator edge = node->out_edges().begin();\n         edge != node->out_edges().end(); ++edge) {\n      for (vector<Node*>::iterator out = (*edge)->outputs_.begin();\n           out != (*edge)->outputs_.end(); ++out) {\n        printf(\"    %s\\n\", (*out)->path().c_str());\n      }\n    }\n    const std::vector<Edge*> validation_edges = node->validation_out_edges();\n    if (!validation_edges.empty()) {\n      printf(\"  validation for:\\n\");\n      for (std::vector<Edge*>::const_iterator edge = validation_edges.begin();\n           edge != validation_edges.end(); ++edge) {\n        for (vector<Node*>::iterator out = (*edge)->outputs_.begin();\n             out != (*edge)->outputs_.end(); ++out) {\n          printf(\"    %s\\n\", (*out)->path().c_str());\n        }\n      }\n    }\n  }\n  return 0;\n}\n\n#if defined(NINJA_HAVE_BROWSE)\nint NinjaMain::ToolBrowse(const Options* options, int argc, char* argv[]) {\n  RunBrowsePython(&state_, ninja_command_, options->input_file, argc, argv);\n  // If we get here, the browse failed.\n  return 1;\n}\n#else\nint NinjaMain::ToolBrowse(const Options*, int, char**) {\n  Fatal(\"browse tool not supported on this platform\");\n  return 1;\n}\n#endif\n\n#if defined(_WIN32)\nint NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) {\n  // Reset getopt: push one argument onto the front of argv, reset optind.\n  argc++;\n  argv--;\n  optind = 0;\n  return MSVCHelperMain(argc, argv);\n}\n#endif\n\nint ToolTargetsList(const vector<Node*>& nodes, int depth, int indent) {\n  for (vector<Node*>::const_iterator n = nodes.begin();\n       n != nodes.end();\n       ++n) {\n    for (int i = 0; i < indent; ++i)\n      printf(\"  \");\n    const char* target = (*n)->path().c_str();\n    if ((*n)->in_edge()) {\n      printf(\"%s: %s\\n\", target, (*n)->in_edge()->rule_->name().c_str());\n      if (depth > 1 || depth <= 0)\n        ToolTargetsList((*n)->in_edge()->inputs_, depth - 1, indent + 1);\n    } else {\n      printf(\"%s\\n\", target);\n    }\n  }\n  return 0;\n}\n\nint ToolTargetsSourceList(State* state) {\n  for (vector<Edge*>::iterator e = state->edges_.begin();\n       e != state->edges_.end(); ++e) {\n    for (vector<Node*>::iterator inps = (*e)->inputs_.begin();\n         inps != (*e)->inputs_.end(); ++inps) {\n      if (!(*inps)->in_edge())\n        printf(\"%s\\n\", (*inps)->path().c_str());\n    }\n  }\n  return 0;\n}\n\nint ToolTargetsList(State* state, const string& rule_name) {\n  set<string> rules;\n\n  // Gather the outputs.\n  for (vector<Edge*>::iterator e = state->edges_.begin();\n       e != state->edges_.end(); ++e) {\n    if ((*e)->rule_->name() == rule_name) {\n      for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();\n           out_node != (*e)->outputs_.end(); ++out_node) {\n        rules.insert((*out_node)->path());\n      }\n    }\n  }\n\n  // Print them.\n  for (set<string>::const_iterator i = rules.begin();\n       i != rules.end(); ++i) {\n    printf(\"%s\\n\", (*i).c_str());\n  }\n\n  return 0;\n}\n\nint ToolTargetsList(State* state) {\n  for (vector<Edge*>::iterator e = state->edges_.begin();\n       e != state->edges_.end(); ++e) {\n    for (vector<Node*>::iterator out_node = (*e)->outputs_.begin();\n         out_node != (*e)->outputs_.end(); ++out_node) {\n      printf(\"%s: %s\\n\",\n             (*out_node)->path().c_str(),\n             (*e)->rule_->name().c_str());\n    }\n  }\n  return 0;\n}\n\nint NinjaMain::ToolDeps(const Options* options, int argc, char** argv) {\n  vector<Node*> nodes;\n  if (argc == 0) {\n    for (vector<Node*>::const_iterator ni = deps_log_.nodes().begin();\n         ni != deps_log_.nodes().end(); ++ni) {\n      if (DepsLog::IsDepsEntryLiveFor(*ni))\n        nodes.push_back(*ni);\n    }\n  } else {\n    string err;\n    if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {\n      Error(\"%s\", err.c_str());\n      return 1;\n    }\n  }\n\n  RealDiskInterface disk_interface;\n  for (vector<Node*>::iterator it = nodes.begin(), end = nodes.end();\n       it != end; ++it) {\n    DepsLog::Deps* deps = deps_log_.GetDeps(*it);\n    if (!deps) {\n      printf(\"%s: deps not found\\n\", (*it)->path().c_str());\n      continue;\n    }\n\n    string err;\n    TimeStamp mtime = disk_interface.Stat((*it)->path(), &err);\n    if (mtime == -1)\n      Error(\"%s\", err.c_str());  // Log and ignore Stat() errors;\n    printf(\"%s: #deps %d, deps mtime %\" PRId64 \" (%s)\\n\",\n           (*it)->path().c_str(), deps->node_count, deps->mtime,\n           (!mtime || mtime > deps->mtime ? \"STALE\":\"VALID\"));\n    for (int i = 0; i < deps->node_count; ++i)\n      printf(\"    %s\\n\", deps->nodes[i]->path().c_str());\n    printf(\"\\n\");\n  }\n\n  return 0;\n}\n\nint NinjaMain::ToolMissingDeps(const Options* options, int argc, char** argv) {\n  vector<Node*> nodes;\n  string err;\n  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {\n    Error(\"%s\", err.c_str());\n    return 1;\n  }\n  RealDiskInterface disk_interface;\n  MissingDependencyPrinter printer;\n  MissingDependencyScanner scanner(&printer, &deps_log_, &state_,\n                                   &disk_interface);\n  for (vector<Node*>::iterator it = nodes.begin(); it != nodes.end(); ++it) {\n    scanner.ProcessNode(*it);\n  }\n  scanner.PrintStats();\n  if (scanner.HadMissingDeps())\n    return 3;\n  return 0;\n}\n\nint NinjaMain::ToolTargets(const Options* options, int argc, char* argv[]) {\n  int depth = 1;\n  if (argc >= 1) {\n    string mode = argv[0];\n    if (mode == \"rule\") {\n      string rule;\n      if (argc > 1)\n        rule = argv[1];\n      if (rule.empty())\n        return ToolTargetsSourceList(&state_);\n      else\n        return ToolTargetsList(&state_, rule);\n    } else if (mode == \"depth\") {\n      if (argc > 1)\n        depth = atoi(argv[1]);\n    } else if (mode == \"all\") {\n      return ToolTargetsList(&state_);\n    } else {\n      const char* suggestion =\n          SpellcheckString(mode.c_str(), \"rule\", \"depth\", \"all\", NULL);\n      if (suggestion) {\n        Error(\"unknown target tool mode '%s', did you mean '%s'?\",\n              mode.c_str(), suggestion);\n      } else {\n        Error(\"unknown target tool mode '%s'\", mode.c_str());\n      }\n      return 1;\n    }\n  }\n\n  string err;\n  vector<Node*> root_nodes = state_.RootNodes(&err);\n  if (err.empty()) {\n    return ToolTargetsList(root_nodes, depth, 0);\n  } else {\n    Error(\"%s\", err.c_str());\n    return 1;\n  }\n}\n\nint NinjaMain::ToolRules(const Options* options, int argc, char* argv[]) {\n  // Parse options.\n\n  // The rules tool uses getopt, and expects argv[0] to contain the name of\n  // the tool, i.e. \"rules\".\n  argc++;\n  argv--;\n\n  bool print_description = false;\n\n  optind = 1;\n  int opt;\n  while ((opt = getopt(argc, argv, const_cast<char*>(\"hd\"))) != -1) {\n    switch (opt) {\n    case 'd':\n      print_description = true;\n      break;\n    case 'h':\n    default:\n      printf(\"usage: ninja -t rules [options]\\n\"\n             \"\\n\"\n             \"options:\\n\"\n             \"  -d     also print the description of the rule\\n\"\n             \"  -h     print this message\\n\"\n             );\n    return 1;\n    }\n  }\n  argv += optind;\n  argc -= optind;\n\n  // Print rules\n\n  typedef map<string, std::unique_ptr<const Rule>> Rules;\n  const Rules& rules = state_.bindings_.GetRules();\n  for (Rules::const_iterator i = rules.begin(); i != rules.end(); ++i) {\n    printf(\"%s\", i->first.c_str());\n    if (print_description) {\n      const Rule* rule = i->second.get();\n      const EvalString* description = rule->GetBinding(\"description\");\n      if (description != NULL) {\n        printf(\": %s\", description->Unparse().c_str());\n      }\n    }\n    printf(\"\\n\");\n    fflush(stdout);\n  }\n  return 0;\n}\n\n#ifdef _WIN32\nint NinjaMain::ToolWinCodePage(const Options* options, int argc, char* argv[]) {\n  if (argc != 0) {\n    printf(\"usage: ninja -t wincodepage\\n\");\n    return 1;\n  }\n  printf(\"Build file encoding: %s\\n\", GetACP() == CP_UTF8? \"UTF-8\" : \"ANSI\");\n  return 0;\n}\n#endif\n\nenum PrintCommandMode { PCM_Single, PCM_All };\nvoid PrintCommands(Edge* edge, EdgeSet* seen, PrintCommandMode mode) {\n  if (!edge)\n    return;\n  if (!seen->insert(edge).second)\n    return;\n\n  if (mode == PCM_All) {\n    for (vector<Node*>::iterator in = edge->inputs_.begin();\n         in != edge->inputs_.end(); ++in)\n      PrintCommands((*in)->in_edge(), seen, mode);\n  }\n\n  if (!edge->is_phony())\n    puts(edge->EvaluateCommand().c_str());\n}\n\nint NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) {\n  // The commands tool uses getopt, and expects argv[0] to contain the name of\n  // the tool, i.e. \"commands\".\n  ++argc;\n  --argv;\n\n  PrintCommandMode mode = PCM_All;\n\n  optind = 1;\n  int opt;\n  while ((opt = getopt(argc, argv, const_cast<char*>(\"hs\"))) != -1) {\n    switch (opt) {\n    case 's':\n      mode = PCM_Single;\n      break;\n    case 'h':\n    default:\n      printf(\"usage: ninja -t commands [options] [targets]\\n\"\n\"\\n\"\n\"options:\\n\"\n\"  -s     only print the final command to build [target], not the whole chain\\n\"\n             );\n    return 1;\n    }\n  }\n  argv += optind;\n  argc -= optind;\n\n  vector<Node*> nodes;\n  string err;\n  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {\n    Error(\"%s\", err.c_str());\n    return 1;\n  }\n\n  EdgeSet seen;\n  for (vector<Node*>::iterator in = nodes.begin(); in != nodes.end(); ++in)\n    PrintCommands((*in)->in_edge(), &seen, mode);\n\n  return 0;\n}\n\nint NinjaMain::ToolInputs(const Options* options, int argc, char* argv[]) {\n  // The inputs tool uses getopt, and expects argv[0] to contain the name of\n  // the tool, i.e. \"inputs\".\n  argc++;\n  argv--;\n\n  bool print0 = false;\n  bool shell_escape = true;\n  bool dependency_order = false;\n\n  optind = 1;\n  int opt;\n  const option kLongOptions[] = { { \"help\", no_argument, NULL, 'h' },\n                                  { \"no-shell-escape\", no_argument, NULL, 'E' },\n                                  { \"print0\", no_argument, NULL, '0' },\n                                  { \"dependency-order\", no_argument, NULL,\n                                    'd' },\n                                  { NULL, 0, NULL, 0 } };\n  while ((opt = getopt_long(argc, argv, \"h0Ed\", kLongOptions, NULL)) != -1) {\n    switch (opt) {\n    case 'd':\n      dependency_order = true;\n      break;\n    case 'E':\n      shell_escape = false;\n      break;\n    case '0':\n      print0 = true;\n      break;\n    case 'h':\n    default:\n      // clang-format off\n      printf(\n\"Usage '-t inputs [options] [targets]\\n\"\n\"\\n\"\n\"List all inputs used for a set of targets, sorted in dependency order.\\n\"\n\"Note that by default, results are shell escaped, and sorted alphabetically,\\n\"\n\"and never include validation target paths.\\n\\n\"\n\"Options:\\n\"\n\"  -h, --help          Print this message.\\n\"\n\"  -0, --print0            Use \\\\0, instead of \\\\n as a line terminator.\\n\"\n\"  -E, --no-shell-escape   Do not shell escape the result.\\n\"\n\"  -d, --dependency-order  Sort results by dependency order.\\n\"\n      );\n      // clang-format on\n      return 1;\n    }\n  }\n  argv += optind;\n  argc -= optind;\n\n  std::vector<Node*> nodes;\n  std::string err;\n  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {\n    Error(\"%s\", err.c_str());\n    return 1;\n  }\n\n  InputsCollector collector;\n  for (const Node* node : nodes)\n    collector.VisitNode(node);\n\n  std::vector<std::string> inputs = collector.GetInputsAsStrings(shell_escape);\n  if (!dependency_order)\n    std::sort(inputs.begin(), inputs.end());\n\n  if (print0) {\n    for (const std::string& input : inputs) {\n      fwrite(input.c_str(), input.size(), 1, stdout);\n      fputc('\\0', stdout);\n    }\n    fflush(stdout);\n  } else {\n    for (const std::string& input : inputs)\n      puts(input.c_str());\n  }\n  return 0;\n}\n\nint NinjaMain::ToolMultiInputs(const Options* options, int argc, char* argv[]) {\n  // The inputs tool uses getopt, and expects argv[0] to contain the name of\n  // the tool, i.e. \"inputs\".\n  argc++;\n  argv--;\n\n  optind = 1;\n  int opt;\n  char terminator = '\\n';\n  const char* delimiter = \"\\t\";\n  const option kLongOptions[] = { { \"help\", no_argument, NULL, 'h' },\n                                  { \"delimiter\", required_argument, NULL,\n                                    'd' },\n                                  { \"print0\", no_argument, NULL, '0' },\n                                  { NULL, 0, NULL, 0 } };\n  while ((opt = getopt_long(argc, argv, \"d:h0\", kLongOptions, NULL)) != -1) {\n    switch (opt) {\n    case 'd':\n      delimiter = optarg;\n      break;\n    case '0':\n      terminator = '\\0';\n      break;\n    case 'h':\n    default:\n      // clang-format off\n      printf(\n\"Usage '-t multi-inputs [options] [targets]\\n\"\n\"\\n\"\n\"Print one or more sets of inputs required to build targets, sorted in dependency order.\\n\"\n\"The tool works like inputs tool but with addition of the target for each line.\\n\"\n\"The output will be a series of lines with the following elements:\\n\"\n\"<target> <delimiter> <input> <terminator>\\n\"\n\"Note that a given input may appear for several targets if it is used by more than one targets.\\n\"\n\"Options:\\n\"\n\"  -h, --help                   Print this message.\\n\"\n\"  -d  --delimiter=DELIM        Use DELIM instead of TAB for field delimiter.\\n\"\n\"  -0, --print0                 Use \\\\0, instead of \\\\n as a line terminator.\\n\"\n      );\n      // clang-format on\n      return 1;\n    }\n  }\n  argv += optind;\n  argc -= optind;\n\n  std::vector<Node*> nodes;\n  std::string err;\n  if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {\n    Error(\"%s\", err.c_str());\n    return 1;\n  }\n\n  for (const Node* node : nodes) {\n    InputsCollector collector;\n\n    collector.VisitNode(node);\n    std::vector<std::string> inputs = collector.GetInputsAsStrings();\n\n    for (const std::string& input : inputs) {\n      printf(\"%s%s%s\", node->path().c_str(), delimiter, input.c_str());\n      fputc(terminator, stdout);\n    }\n  }\n\n  return 0;\n}\n\nint NinjaMain::ToolClean(const Options* options, int argc, char* argv[]) {\n  // The clean tool uses getopt, and expects argv[0] to contain the name of\n  // the tool, i.e. \"clean\".\n  argc++;\n  argv--;\n\n  bool generator = false;\n  bool clean_rules = false;\n\n  optind = 1;\n  int opt;\n  while ((opt = getopt(argc, argv, const_cast<char*>(\"hgr\"))) != -1) {\n    switch (opt) {\n    case 'g':\n      generator = true;\n      break;\n    case 'r':\n      clean_rules = true;\n      break;\n    case 'h':\n    default:\n      printf(\"usage: ninja -t clean [options] [targets]\\n\"\n\"\\n\"\n\"options:\\n\"\n\"  -g     also clean files marked as ninja generator output\\n\"\n\"  -r     interpret targets as a list of rules to clean instead\\n\"\n             );\n    return 1;\n    }\n  }\n  argv += optind;\n  argc -= optind;\n\n  if (clean_rules && argc == 0) {\n    Error(\"expected a rule to clean\");\n    return 1;\n  }\n\n  Cleaner cleaner(&state_, config_, &disk_interface_);\n  if (argc >= 1) {\n    if (clean_rules)\n      return cleaner.CleanRules(argc, argv);\n    else\n      return cleaner.CleanTargets(argc, argv);\n  } else {\n    return cleaner.CleanAll(generator);\n  }\n}\n\nint NinjaMain::ToolCleanDead(const Options* options, int argc, char* argv[]) {\n  Cleaner cleaner(&state_, config_, &disk_interface_);\n  return cleaner.CleanDead(build_log_.entries());\n}\n\nenum EvaluateCommandMode {\n  ECM_NORMAL,\n  ECM_EXPAND_RSPFILE\n};\nstd::string EvaluateCommandWithRspfile(const Edge* edge,\n                                       const EvaluateCommandMode mode) {\n  string command = edge->EvaluateCommand();\n  if (mode == ECM_NORMAL)\n    return command;\n\n  string rspfile = edge->GetUnescapedRspfile();\n  if (rspfile.empty())\n    return command;\n\n  size_t index = command.find(rspfile);\n  if (index == 0 || index == string::npos ||\n      (command[index - 1] != '@' &&\n       command.find(\"--option-file=\") != index - 14 &&\n       command.find(\"-f \") != index - 3))\n    return command;\n\n  string rspfile_content = edge->GetBinding(\"rspfile_content\");\n  size_t newline_index = 0;\n  while ((newline_index = rspfile_content.find('\\n', newline_index)) !=\n         string::npos) {\n    rspfile_content.replace(newline_index, 1, 1, ' ');\n    ++newline_index;\n  }\n  if (command[index - 1] == '@') {\n    command.replace(index - 1, rspfile.length() + 1, rspfile_content);\n  } else if (command.find(\"-f \") == index - 3) {\n    command.replace(index - 3, rspfile.length() + 3, rspfile_content);\n  } else {  // --option-file syntax\n    command.replace(index - 14, rspfile.length() + 14, rspfile_content);\n  }\n  return command;\n}\n\nvoid PrintCompdbObjectsForEdge(std::string const& directory, const Edge* const edge,\n                               const EvaluateCommandMode eval_mode) {\n  const auto& command = EvaluateCommandWithRspfile(edge, eval_mode);\n  bool first = true;\n\n  for (const Node* input : edge->inputs_) {\n    if (!first) {\n      putchar(',');\n    }\n\n    printf(\"\\n  {\\n    \\\"directory\\\": \\\"\");\n    PrintJSONString(directory);\n    printf(\"\\\",\\n    \\\"command\\\": \\\"\");\n    PrintJSONString(command);\n    printf(\"\\\",\\n    \\\"file\\\": \\\"\");\n    PrintJSONString(input->path());\n    printf(\"\\\",\\n    \\\"output\\\": \\\"\");\n    PrintJSONString(edge->outputs_[0]->path());\n    printf(\"\\\"\\n  }\");\n    first = false;\n  }\n}\n\nint NinjaMain::ToolCompilationDatabase(const Options* options, int argc,\n                                       char* argv[]) {\n  // The compdb tool uses getopt, and expects argv[0] to contain the name of\n  // the tool, i.e. \"compdb\".\n  argc++;\n  argv--;\n\n  EvaluateCommandMode eval_mode = ECM_NORMAL;\n\n  optind = 1;\n  int opt;\n  while ((opt = getopt(argc, argv, const_cast<char*>(\"hx\"))) != -1) {\n    switch(opt) {\n      case 'x':\n        eval_mode = ECM_EXPAND_RSPFILE;\n        break;\n\n      case 'h':\n      default:\n        printf(\n            \"usage: ninja -t compdb [options] [rules]\\n\"\n            \"\\n\"\n            \"options:\\n\"\n            \"  -x     expand @rspfile style response file invocations\\n\"\n            );\n        return 1;\n    }\n  }\n  argv += optind;\n  argc -= optind;\n\n  bool first = true;\n\n  std::string directory = GetWorkingDirectory();\n  putchar('[');\n  for (const Edge* edge : state_.edges_) {\n    if (edge->inputs_.empty())\n      continue;\n    if (argc == 0) {\n      if (!first) {\n        putchar(',');\n      }\n      PrintCompdbObjectsForEdge(directory, edge, eval_mode);\n      first = false;\n    } else {\n      for (int i = 0; i != argc; ++i) {\n        if (edge->rule_->name() == argv[i]) {\n          if (!first) {\n            putchar(',');\n          }\n          PrintCompdbObjectsForEdge(directory, edge, eval_mode);\n          first = false;\n        }\n      }\n    }\n  }\n\n  puts(\"\\n]\");\n  return 0;\n}\n\nint NinjaMain::ToolRecompact(const Options* options, int argc, char* argv[]) {\n  if (!EnsureBuildDirExists())\n    return 1;\n\n  if (!OpenBuildLog(/*recompact_only=*/true) ||\n      !OpenDepsLog(/*recompact_only=*/true))\n    return 1;\n\n  return 0;\n}\n\nint NinjaMain::ToolRestat(const Options* options, int argc, char* argv[]) {\n  // The restat tool uses getopt, and expects argv[0] to contain the name of the\n  // tool, i.e. \"restat\"\n  argc++;\n  argv--;\n\n  optind = 1;\n  int opt;\n  const option kLongOptions[] = { { \"builddir\", required_argument, nullptr,\n                                    'b' },\n                                  { \"help\", no_argument, nullptr, 'h' },\n                                  { nullptr, 0, nullptr, 0 } };\n  while ((opt = getopt_long(argc, argv, const_cast<char*>(\"h\"), kLongOptions,\n                            nullptr)) != -1) {\n    switch (opt) {\n    case 'b':\n      build_dir_ = optarg;\n      break;\n    case 'h':\n    default:\n      printf(\"usage: ninja -t restat [--builddir=DIR] [outputs]\\n\");\n      return 1;\n    }\n  }\n  argv += optind;\n  argc -= optind;\n\n  string log_path = \".ninja_log\";\n  if (!build_dir_.empty())\n    log_path = build_dir_ + \"/\" + log_path;\n\n  string err;\n  const LoadStatus status = build_log_.Load(log_path, &err);\n  if (status == LOAD_ERROR) {\n    Error(\"loading build log %s: %s\", log_path.c_str(), err.c_str());\n    return EXIT_FAILURE;\n  }\n  if (status == LOAD_NOT_FOUND) {\n    // Nothing to restat, ignore this\n    return EXIT_SUCCESS;\n  }\n  if (!err.empty()) {\n    // Hack: Load() can return a warning via err by returning LOAD_SUCCESS.\n    Warning(\"%s\", err.c_str());\n    err.clear();\n  }\n\n  bool success = build_log_.Restat(log_path, disk_interface_, argc, argv, &err);\n  if (!success) {\n    Error(\"failed recompaction: %s\", err.c_str());\n    return EXIT_FAILURE;\n  }\n\n  if (!config_.dry_run) {\n    if (!build_log_.OpenForWrite(log_path, *this, &err)) {\n      Error(\"opening build log: %s\", err.c_str());\n      return EXIT_FAILURE;\n    }\n  }\n\n  return EXIT_SUCCESS;\n}\n\nstruct CompdbTargets {\n  enum class Action { kDisplayHelpAndExit, kEmitCommands };\n\n  Action action;\n  EvaluateCommandMode eval_mode = ECM_NORMAL;\n\n  std::vector<std::string> targets;\n\n  static CompdbTargets CreateFromArgs(int argc, char* argv[]) {\n    //\n    // grammar:\n    //     ninja -t compdb-targets [-hx] target [targets]\n    //\n    CompdbTargets ret;\n\n    // getopt_long() expects argv[0] to contain the name of\n    // the tool, i.e. \"compdb-targets\".\n    argc++;\n    argv--;\n\n    // Phase 1: parse options:\n    optind = 1;  // see `man 3 getopt` for documentation on optind\n    int opt;\n    while ((opt = getopt(argc, argv, const_cast<char*>(\"hx\"))) != -1) {\n      switch (opt) {\n      case 'x':\n        ret.eval_mode = ECM_EXPAND_RSPFILE;\n        break;\n      case 'h':\n      default:\n        ret.action = CompdbTargets::Action::kDisplayHelpAndExit;\n        return ret;\n      }\n    }\n\n    // Phase 2: parse operands:\n    int const targets_begin = optind;\n    int const targets_end = argc;\n\n    if (targets_begin == targets_end) {\n      Error(\"compdb-targets expects the name of at least one target\");\n      ret.action = CompdbTargets::Action::kDisplayHelpAndExit;\n    } else {\n      ret.action = CompdbTargets::Action::kEmitCommands;\n      for (int i = targets_begin; i < targets_end; ++i) {\n        ret.targets.push_back(argv[i]);\n      }\n    }\n\n    return ret;\n  }\n};\n\nvoid PrintCompdb(std::string const& directory, std::vector<Edge*> const& edges,\n                 const EvaluateCommandMode eval_mode) {\n  putchar('[');\n\n  bool first = true;\n  for (const Edge* edge : edges) {\n    if (edge->is_phony() || edge->inputs_.empty())\n      continue;\n    if (!first)\n      putchar(',');\n    PrintCompdbObjectsForEdge(directory, edge, eval_mode);\n    first = false;\n  }\n\n  puts(\"\\n]\");\n}\n\nint NinjaMain::ToolCompilationDatabaseForTargets(const Options* options,\n                                                 int argc, char* argv[]) {\n  auto compdb = CompdbTargets::CreateFromArgs(argc, argv);\n\n  switch (compdb.action) {\n  case CompdbTargets::Action::kDisplayHelpAndExit: {\n    printf(\n        \"usage: ninja -t compdb [-hx] target [targets]\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h     display this help message\\n\"\n        \"  -x     expand @rspfile style response file invocations\\n\");\n    return 1;\n  }\n\n  case CompdbTargets::Action::kEmitCommands: {\n    CommandCollector collector;\n\n    for (const std::string& target_arg : compdb.targets) {\n      std::string err;\n      Node* node = CollectTarget(target_arg.c_str(), &err);\n      if (!node) {\n        Fatal(\"%s\", err.c_str());\n        return 1;\n      }\n      if (!node->in_edge()) {\n        Fatal(\n            \"'%s' is not a target \"\n            \"(i.e. it is not an output of any `build` statement)\",\n            node->path().c_str());\n      }\n      collector.CollectFrom(node);\n    }\n\n    std::string directory = GetWorkingDirectory();\n    PrintCompdb(directory, collector.in_edges, compdb.eval_mode);\n  } break;\n  }\n\n  return 0;\n}\n\nint NinjaMain::ToolUrtle(const Options* options, int argc, char** argv) {\n  // RLE encoded.\n  const char* urtle =\n\" 13 ,3;2!2;\\n8 ,;<11!;\\n5 `'<10!(2`'2!\\n11 ,6;, `\\\\. `\\\\9 .,c13$ec,.\\n6 \"\n\",2;11!>; `. ,;!2> .e8$2\\\".2 \\\"?7$e.\\n <:<8!'` 2.3,.2` ,3!' ;,(?7\\\";2!2'<\"\n\"; `?6$PF ,;,\\n2 `'4!8;<!3'`2 3! ;,`'2`2'3!;4!`2.`!;2 3,2 .<!2'`).\\n5 3`5\"\n\"'2`9 `!2 `4!><3;5! J2$b,`!>;2!:2!`,d?b`!>\\n26 `'-;,(<9!> $F3 )3.:!.2 d\\\"\"\n\"2 ) !>\\n30 7`2'<3!- \\\"=-='5 .2 `2-=\\\",!>\\n25 .ze9$er2 .,cd16$bc.'\\n22 .e\"\n\"14$,26$.\\n21 z45$c .\\n20 J50$c\\n20 14$P\\\"`?34$b\\n20 14$ dbc `2\\\"?22$?7$c\"\n\"\\n20 ?18$c.6 4\\\"8?4\\\" c8$P\\n9 .2,.8 \\\"20$c.3 ._14 J9$\\n .2,2c9$bec,.2 `?\"\n\"21$c.3`4%,3%,3 c8$P\\\"\\n22$c2 2\\\"?21$bc2,.2` .2,c7$P2\\\",cb\\n23$b bc,.2\\\"2\"\n\"?14$2F2\\\"5?2\\\",J5$P\\\" ,zd3$\\n24$ ?$3?%3 `2\\\"2?12$bcucd3$P3\\\"2 2=7$\\n23$P\"\n\"\\\" ,3;<5!>2;,. `4\\\"6?2\\\"2 ,9;, `\\\"?2$\\n\";\n  int count = 0;\n  for (const char* p = urtle; *p; p++) {\n    if ('0' <= *p && *p <= '9') {\n      count = count*10 + *p - '0';\n    } else {\n      for (int i = 0; i < max(count, 1); ++i)\n        printf(\"%c\", *p);\n      count = 0;\n    }\n  }\n  return 0;\n}\n\n/// Find the function to execute for \\a tool_name and return it via \\a func.\n/// Returns a Tool, or NULL if Ninja should exit.\nconst Tool* ChooseTool(const string& tool_name) {\n  static const Tool kTools[] = {\n    { \"browse\", \"browse dependency graph in a web browser\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse },\n#ifdef _WIN32\n    { \"msvc\", \"build helper for MSVC cl.exe (DEPRECATED)\",\n      Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC },\n#endif\n    { \"clean\", \"clean built files\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean },\n    { \"commands\", \"list all commands required to rebuild given targets\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands },\n    { \"inputs\", \"list all inputs required to rebuild given targets\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolInputs},\n    { \"multi-inputs\", \"print one or more sets of inputs required to build targets\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolMultiInputs},\n    { \"deps\", \"show dependencies stored in the deps log\",\n      Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps },\n    { \"missingdeps\", \"check deps log dependencies on generated files\",\n      Tool::RUN_AFTER_LOGS, &NinjaMain::ToolMissingDeps },\n    { \"graph\", \"output graphviz dot file for targets\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph },\n    { \"query\", \"show inputs/outputs for a path\",\n      Tool::RUN_AFTER_LOGS, &NinjaMain::ToolQuery },\n    { \"targets\",  \"list targets by their rule or depth in the DAG\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolTargets },\n    { \"compdb\",  \"dump JSON compilation database to stdout\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase },\n    { \"compdb-targets\",\n      \"dump JSON compilation database for a given list of targets to stdout\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabaseForTargets },\n    { \"recompact\",  \"recompacts ninja-internal data structures\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRecompact },\n    { \"restat\",  \"restats all outputs in the build log\",\n      Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolRestat },\n    { \"rules\",  \"list all rules\",\n      Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRules },\n    { \"cleandead\",  \"clean built files that are no longer produced by the manifest\",\n      Tool::RUN_AFTER_LOGS, &NinjaMain::ToolCleanDead },\n    { \"urtle\", NULL,\n      Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolUrtle },\n#ifdef _WIN32\n    { \"wincodepage\", \"print the Windows code page used by ninja\",\n      Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolWinCodePage },\n#endif\n    { NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL }\n  };\n\n  if (tool_name == \"list\") {\n    printf(\"ninja subtools:\\n\");\n    for (const Tool* tool = &kTools[0]; tool->name; ++tool) {\n      if (tool->desc)\n        printf(\"%11s  %s\\n\", tool->name, tool->desc);\n    }\n    return NULL;\n  }\n\n  for (const Tool* tool = &kTools[0]; tool->name; ++tool) {\n    if (tool->name == tool_name)\n      return tool;\n  }\n\n  vector<const char*> words;\n  for (const Tool* tool = &kTools[0]; tool->name; ++tool)\n    words.push_back(tool->name);\n  const char* suggestion = SpellcheckStringV(tool_name, words);\n  if (suggestion) {\n    Fatal(\"unknown tool '%s', did you mean '%s'?\",\n          tool_name.c_str(), suggestion);\n  } else {\n    Fatal(\"unknown tool '%s'\", tool_name.c_str());\n  }\n  return NULL;  // Not reached.\n}\n\n/// Enable a debugging mode.  Returns false if Ninja should exit instead\n/// of continuing.\nbool DebugEnable(const string& name) {\n  if (name == \"list\") {\n    printf(\"debugging modes:\\n\"\n\"  stats        print operation counts/timing info\\n\"\n\"  explain      explain what caused a command to execute\\n\"\n\"  keepdepfile  don't delete depfiles after they're read by ninja\\n\"\n\"  keeprsp      don't delete @response files on success\\n\"\n#ifdef _WIN32\n\"  nostatcache  don't batch stat() calls per directory and cache them\\n\"\n#endif\n\"multiple modes can be enabled via -d FOO -d BAR\\n\");\n    return false;\n  } else if (name == \"stats\") {\n    g_metrics = new Metrics;\n    return true;\n  } else if (name == \"explain\") {\n    g_explaining = true;\n    return true;\n  } else if (name == \"keepdepfile\") {\n    g_keep_depfile = true;\n    return true;\n  } else if (name == \"keeprsp\") {\n    g_keep_rsp = true;\n    return true;\n  } else if (name == \"nostatcache\") {\n    g_experimental_statcache = false;\n    return true;\n  } else {\n    const char* suggestion =\n        SpellcheckString(name.c_str(),\n                         \"stats\", \"explain\", \"keepdepfile\", \"keeprsp\",\n                         \"nostatcache\", NULL);\n    if (suggestion) {\n      Error(\"unknown debug setting '%s', did you mean '%s'?\",\n            name.c_str(), suggestion);\n    } else {\n      Error(\"unknown debug setting '%s'\", name.c_str());\n    }\n    return false;\n  }\n}\n\n/// Set a warning flag.  Returns false if Ninja should exit instead of\n/// continuing.\nbool WarningEnable(const string& name, Options* options) {\n  if (name == \"list\") {\n    printf(\"warning flags:\\n\"\n\"  phonycycle={err,warn}  phony build statement references itself\\n\"\n    );\n    return false;\n  } else if (name == \"phonycycle=err\") {\n    options->phony_cycle_should_err = true;\n    return true;\n  } else if (name == \"phonycycle=warn\") {\n    options->phony_cycle_should_err = false;\n    return true;\n  } else if (name == \"dupbuild=err\" ||\n             name == \"dupbuild=warn\") {\n    Warning(\"deprecated warning 'dupbuild'\");\n    return true;\n  } else if (name == \"depfilemulti=err\" ||\n             name == \"depfilemulti=warn\") {\n    Warning(\"deprecated warning 'depfilemulti'\");\n    return true;\n  } else {\n    const char* suggestion = SpellcheckString(name.c_str(), \"phonycycle=err\",\n                                              \"phonycycle=warn\", nullptr);\n    if (suggestion) {\n      Error(\"unknown warning flag '%s', did you mean '%s'?\",\n            name.c_str(), suggestion);\n    } else {\n      Error(\"unknown warning flag '%s'\", name.c_str());\n    }\n    return false;\n  }\n}\n\nbool NinjaMain::OpenBuildLog(bool recompact_only) {\n  string log_path = \".ninja_log\";\n  if (!build_dir_.empty())\n    log_path = build_dir_ + \"/\" + log_path;\n\n  string err;\n  const LoadStatus status = build_log_.Load(log_path, &err);\n  if (status == LOAD_ERROR) {\n    Error(\"loading build log %s: %s\", log_path.c_str(), err.c_str());\n    return false;\n  }\n  if (!err.empty()) {\n    // Hack: Load() can return a warning via err by returning LOAD_SUCCESS.\n    Warning(\"%s\", err.c_str());\n    err.clear();\n  }\n\n  if (recompact_only) {\n    if (status == LOAD_NOT_FOUND) {\n      return true;\n    }\n    bool success = build_log_.Recompact(log_path, *this, &err);\n    if (!success)\n      Error(\"failed recompaction: %s\", err.c_str());\n    return success;\n  }\n\n  if (!config_.dry_run) {\n    if (!build_log_.OpenForWrite(log_path, *this, &err)) {\n      Error(\"opening build log: %s\", err.c_str());\n      return false;\n    }\n  }\n\n  return true;\n}\n\n/// Open the deps log: load it, then open for writing.\n/// @return false on error.\nbool NinjaMain::OpenDepsLog(bool recompact_only) {\n  string path = \".ninja_deps\";\n  if (!build_dir_.empty())\n    path = build_dir_ + \"/\" + path;\n\n  string err;\n  const LoadStatus status = deps_log_.Load(path, &state_, &err);\n  if (status == LOAD_ERROR) {\n    Error(\"loading deps log %s: %s\", path.c_str(), err.c_str());\n    return false;\n  }\n  if (!err.empty()) {\n    // Hack: Load() can return a warning via err by returning LOAD_SUCCESS.\n    Warning(\"%s\", err.c_str());\n    err.clear();\n  }\n\n  if (recompact_only) {\n    if (status == LOAD_NOT_FOUND) {\n      return true;\n    }\n    bool success = deps_log_.Recompact(path, &err);\n    if (!success)\n      Error(\"failed recompaction: %s\", err.c_str());\n    return success;\n  }\n\n  if (!config_.dry_run) {\n    if (!deps_log_.OpenForWrite(path, &err)) {\n      Error(\"opening deps log: %s\", err.c_str());\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid NinjaMain::DumpMetrics() {\n  g_metrics->Report();\n\n  printf(\"\\n\");\n  int count = (int)state_.paths_.size();\n  int buckets = (int)state_.paths_.bucket_count();\n  printf(\"path->node hash load %.2f (%d entries / %d buckets)\\n\",\n         count / (double) buckets, count, buckets);\n}\n\nbool NinjaMain::EnsureBuildDirExists() {\n  build_dir_ = state_.bindings_.LookupVariable(\"builddir\");\n  if (!build_dir_.empty() && !config_.dry_run) {\n    if (!disk_interface_.MakeDirs(build_dir_ + \"/.\") && errno != EEXIST) {\n      Error(\"creating build directory %s: %s\",\n            build_dir_.c_str(), strerror(errno));\n      return false;\n    }\n  }\n  return true;\n}\n\nstd::unique_ptr<Jobserver::Client> NinjaMain::SetupJobserverClient(\n    Status* status) {\n  // Empty result by default.\n  std::unique_ptr<Jobserver::Client> result;\n\n  // If dry-run or explicit job count, don't even look at MAKEFLAGS\n  if (config_.disable_jobserver_client)\n    return result;\n\n  const char* makeflags = getenv(\"MAKEFLAGS\");\n  if (!makeflags) {\n    // MAKEFLAGS is not defined.\n    return result;\n  }\n\n  std::string err;\n  Jobserver::Config jobserver_config;\n  if (!Jobserver::ParseNativeMakeFlagsValue(makeflags, &jobserver_config,\n                                            &err)) {\n    // MAKEFLAGS is defined but could not be parsed correctly.\n    if (config_.verbosity > BuildConfig::QUIET)\n      status->Warning(\"Ignoring jobserver: %s [%s]\", err.c_str(), makeflags);\n    return result;\n  }\n\n  if (!jobserver_config.HasMode()) {\n    // MAKEFLAGS is defined, but does not describe a jobserver mode.\n    return result;\n  }\n\n  if (config_.verbosity > BuildConfig::NO_STATUS_UPDATE) {\n    status->Info(\"Jobserver mode detected: %s\", makeflags);\n  }\n\n  result = Jobserver::Client::Create(jobserver_config, &err);\n  if (!result.get()) {\n    // Jobserver client initialization failed !?\n    if (config_.verbosity > BuildConfig::QUIET)\n      status->Error(\"Could not initialize jobserver: %s\", err.c_str());\n  }\n  return result;\n}\n\nExitStatus NinjaMain::RunBuild(int argc, char** argv, Status* status) {\n  std::string err;\n  std::vector<Node*> targets;\n  if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) {\n    status->Error(\"%s\", err.c_str());\n    return ExitFailure;\n  }\n\n  disk_interface_.AllowStatCache(g_experimental_statcache);\n\n  // Detect jobserver context and inject Jobserver::Client into the builder\n  // if needed.\n  std::unique_ptr<Jobserver::Client> jobserver_client =\n      SetupJobserverClient(status);\n\n  Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_,\n                  status, start_time_millis_);\n\n  if (jobserver_client.get()) {\n    builder.SetJobserverClient(std::move(jobserver_client));\n  }\n\n  for (size_t i = 0; i < targets.size(); ++i) {\n    if (!builder.AddTarget(targets[i], &err)) {\n      if (!err.empty()) {\n        status->Error(\"%s\", err.c_str());\n        return ExitFailure;\n      } else {\n        // Added a target that is already up-to-date; not really\n        // an error.\n      }\n    }\n  }\n\n  // Make sure restat rules do not see stale timestamps.\n  disk_interface_.AllowStatCache(false);\n\n  if (builder.AlreadyUpToDate()) {\n    if (config_.verbosity != BuildConfig::NO_STATUS_UPDATE) {\n      status->Info(\"no work to do.\");\n    }\n    return ExitSuccess;\n  }\n\n  ExitStatus exit_status = builder.Build(&err);\n  if (exit_status != ExitSuccess) {\n    status->Info(\"build stopped: %s.\", err.c_str());\n    if (err.find(\"interrupted by user\") != string::npos) {\n      return ExitInterrupted;\n    }\n  }\n\n  return exit_status;\n}\n\n#ifdef _MSC_VER\n\n/// This handler processes fatal crashes that you can't catch\n/// Test example: C++ exception in a stack-unwind-block\n/// Real-world example: ninja launched a compiler to process a tricky\n/// C++ input file. The compiler got itself into a state where it\n/// generated 3 GB of output and caused ninja to crash.\nvoid TerminateHandler() {\n  CreateWin32MiniDump(NULL);\n  Fatal(\"terminate handler called\");\n}\n\n/// On Windows, we want to prevent error dialogs in case of exceptions.\n/// This function handles the exception, and writes a minidump.\nint ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {\n  Error(\"exception: 0x%X\", code);  // e.g. EXCEPTION_ACCESS_VIOLATION\n  fflush(stderr);\n  CreateWin32MiniDump(ep);\n  return EXCEPTION_EXECUTE_HANDLER;\n}\n\n#endif  // _MSC_VER\n\nclass DeferGuessParallelism {\n public:\n  bool needGuess;\n  BuildConfig* config;\n\n  DeferGuessParallelism(BuildConfig* config)\n      : needGuess(true), config(config) {}\n\n  void Refresh() {\n    if (needGuess) {\n      needGuess = false;\n      config->parallelism = GuessParallelism();\n    }\n  }\n  ~DeferGuessParallelism() { Refresh(); }\n};\n\n/// Parse argv for command-line options.\n/// Returns an exit code, or -1 if Ninja should continue.\nint ReadFlags(int* argc, char*** argv,\n              Options* options, BuildConfig* config) {\n  DeferGuessParallelism deferGuessParallelism(config);\n\n  enum { OPT_VERSION = 1, OPT_QUIET = 2 };\n  const option kLongOptions[] = {\n    { \"help\", no_argument, NULL, 'h' },\n    { \"version\", no_argument, NULL, OPT_VERSION },\n    { \"verbose\", no_argument, NULL, 'v' },\n    { \"quiet\", no_argument, NULL, OPT_QUIET },\n    { NULL, 0, NULL, 0 }\n  };\n\n  int opt;\n  while (!options->tool &&\n         (opt = getopt_long(*argc, *argv, \"d:f:j:k:l:nt:vw:C:h\", kLongOptions,\n                            NULL)) != -1) {\n    switch (opt) {\n      case 'd':\n        if (!DebugEnable(optarg))\n          return 1;\n        break;\n      case 'f':\n        options->input_file = optarg;\n        break;\n      case 'j': {\n        char* end;\n        long value = strtol(optarg, &end, 10);\n        if (*end != 0 || value < 0)\n          Fatal(\"invalid -j parameter\");\n\n        // We want to run N jobs in parallel. For N = 0, INT_MAX\n        // is close enough to infinite for most sane builds.\n        config->parallelism =\n            static_cast<int>((value > 0 && value < INT_MAX) ? value : INT_MAX);\n        config->disable_jobserver_client = true;\n        deferGuessParallelism.needGuess = false;\n        break;\n      }\n      case 'k': {\n        char* end;\n        long value = strtol(optarg, &end, 10);\n        if (*end != 0)\n          Fatal(\"-k parameter not numeric; did you mean -k 0?\");\n\n        // We want to go until N jobs fail, which means we should allow\n        // N failures and then stop.  For N <= 0, INT_MAX is close enough\n        // to infinite for most sane builds.\n        config->failures_allowed =\n            static_cast<int>((value > 0 && value < INT_MAX) ? value : INT_MAX);\n        break;\n      }\n      case 'l': {\n        char* end;\n        double value = strtod(optarg, &end);\n        if (end == optarg)\n          Fatal(\"-l parameter not numeric: did you mean -l 0.0?\");\n        config->max_load_average = value;\n        break;\n      }\n      case 'n':\n        config->dry_run = true;\n        config->disable_jobserver_client = true;\n        break;\n      case 't':\n        options->tool = ChooseTool(optarg);\n        if (!options->tool)\n          return 0;\n        break;\n      case 'v':\n        config->verbosity = BuildConfig::VERBOSE;\n        break;\n      case OPT_QUIET:\n        config->verbosity = BuildConfig::NO_STATUS_UPDATE;\n        break;\n      case 'w':\n        if (!WarningEnable(optarg, options))\n          return 1;\n        break;\n      case 'C':\n        options->working_dir = optarg;\n        break;\n      case OPT_VERSION:\n        printf(\"%s\\n\", kNinjaVersion);\n        return 0;\n      case 'h':\n      default:\n        deferGuessParallelism.Refresh();\n        Usage(*config);\n        return 1;\n    }\n  }\n  *argv += optind;\n  *argc -= optind;\n\n  return -1;\n}\n\nNORETURN void real_main(int argc, char** argv) {\n  // Use exit() instead of return in this function to avoid potentially\n  // expensive cleanup when destructing NinjaMain.\n  BuildConfig config;\n  Options options = {};\n  options.input_file = \"build.ninja\";\n\n  setvbuf(stdout, NULL, _IOLBF, BUFSIZ);\n  const char* ninja_command = argv[0];\n\n  int exit_code = ReadFlags(&argc, &argv, &options, &config);\n  if (exit_code >= 0)\n    exit(exit_code);\n\n  Status* status = Status::factory(config);\n\n  if (options.working_dir) {\n    // The formatting of this string, complete with funny quotes, is\n    // so Emacs can properly identify that the cwd has changed for\n    // subsequent commands.\n    // Don't print this if a tool is being used, so that tool output\n    // can be piped into a file without this string showing up.\n    if (!options.tool && config.verbosity != BuildConfig::NO_STATUS_UPDATE)\n      status->Info(\"Entering directory `%s'\", options.working_dir);\n    if (chdir(options.working_dir) < 0) {\n      Fatal(\"chdir to '%s' - %s\", options.working_dir, strerror(errno));\n    }\n  }\n\n  if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) {\n    // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed\n    // by other tools.\n    NinjaMain ninja(ninja_command, config);\n    exit((ninja.*options.tool->func)(&options, argc, argv));\n  }\n\n  // Limit number of rebuilds, to prevent infinite loops.\n  const int kCycleLimit = 100;\n  for (int cycle = 1; cycle <= kCycleLimit; ++cycle) {\n    NinjaMain ninja(ninja_command, config);\n\n    ManifestParserOptions parser_opts;\n    if (options.phony_cycle_should_err) {\n      parser_opts.phony_cycle_action_ = kPhonyCycleActionError;\n    }\n    ManifestParser parser(&ninja.state_, &ninja.disk_interface_, parser_opts);\n    string err;\n    if (!parser.Load(options.input_file, &err)) {\n      status->Error(\"%s\", err.c_str());\n      exit(1);\n    }\n\n    if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD)\n      exit((ninja.*options.tool->func)(&options, argc, argv));\n\n    if (!ninja.EnsureBuildDirExists())\n      exit(1);\n\n    if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog())\n      exit(1);\n\n    if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS)\n      exit((ninja.*options.tool->func)(&options, argc, argv));\n\n    // Attempt to rebuild the manifest before building anything else\n    if (ninja.RebuildManifest(options.input_file, &err, status)) {\n      // In dry_run mode the regeneration will succeed without changing the\n      // manifest forever. Better to return immediately.\n      if (config.dry_run)\n        exit(0);\n      // Start the build over with the new manifest.\n      continue;\n    } else if (!err.empty()) {\n      status->Error(\"rebuilding '%s': %s\", options.input_file, err.c_str());\n      exit(1);\n    }\n\n    ninja.ParsePreviousElapsedTimes();\n\n    ExitStatus result = ninja.RunBuild(argc, argv, status);\n    if (g_metrics)\n      ninja.DumpMetrics();\n    exit(result);\n  }\n\n  status->Error(\"manifest '%s' still dirty after %d tries, perhaps system time is not set\",\n      options.input_file, kCycleLimit);\n  exit(1);\n}\n\n}  // anonymous namespace\n\nint main(int argc, char** argv) {\n#if defined(_MSC_VER)\n  // Set a handler to catch crashes not caught by the __try..__except\n  // block (e.g. an exception in a stack-unwind-block).\n  std::set_terminate(TerminateHandler);\n  __try {\n    // Running inside __try ... __except suppresses any Windows error\n    // dialogs for errors such as bad_alloc.\n    real_main(argc, argv);\n  }\n  __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {\n    // Common error situations return exitCode=1. 2 was chosen to\n    // indicate a more serious problem.\n    return 2;\n  }\n#else\n  real_main(argc, argv);\n#endif\n}\n"
  },
  {
    "path": "src/ninja_test.cc",
    "content": "// Copyright 2013 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <gtest/gtest.h>\n\nint main(int argc, char **argv) {\n  testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "src/parser.cc",
    "content": "// Copyright 2018 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"parser.h\"\n\n#include \"disk_interface.h\"\n#include \"metrics.h\"\n\nusing namespace std;\n\nbool Parser::Load(const string& filename, string* err, Lexer* parent) {\n  // If |parent| is not NULL, metrics collection has been started by a parent\n  // Parser::Load() in our call stack. Do not start a new one here to avoid\n  // over-counting parsing times.\n  METRIC_RECORD_IF(\".ninja parse\", parent == NULL);\n  string contents;\n  string read_err;\n  if (file_reader_->ReadFile(filename, &contents, &read_err) !=\n      FileReader::Okay) {\n    *err = \"loading '\" + filename + \"': \" + read_err;\n    if (parent)\n      parent->Error(string(*err), err);\n    return false;\n  }\n\n  return Parse(filename, contents, err);\n}\n\nbool Parser::ExpectToken(Lexer::Token expected, string* err) {\n  Lexer::Token token = lexer_.ReadToken();\n  if (token != expected) {\n    string message = string(\"expected \") + Lexer::TokenName(expected);\n    message += string(\", got \") + Lexer::TokenName(token);\n    message += Lexer::TokenErrorHint(expected);\n    return lexer_.Error(message, err);\n  }\n  return true;\n}\n"
  },
  {
    "path": "src/parser.h",
    "content": "// Copyright 2018 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_PARSER_H_\n#define NINJA_PARSER_H_\n\n#include <string>\n\n#include \"lexer.h\"\n\nstruct FileReader;\nstruct State;\n\n/// Base class for parsers.\nstruct Parser {\n  Parser(State* state, FileReader* file_reader)\n      : state_(state), file_reader_(file_reader) {}\n  virtual ~Parser() {}\n\n  /// Load and parse a file.\n  bool Load(const std::string& filename, std::string* err, Lexer* parent = NULL);\n\nprotected:\n  /// If the next token is not \\a expected, produce an error string\n  /// saying \"expected foo, got bar\".\n  bool ExpectToken(Lexer::Token expected, std::string* err);\n\n  State* state_;\n  FileReader* file_reader_;\n  Lexer lexer_;\n\nprivate:\n  /// Parse a file, given its contents as a string.\n  virtual bool Parse(const std::string& filename, const std::string& input,\n                     std::string* err) = 0;\n};\n\n#endif  // NINJA_PARSER_H_\n"
  },
  {
    "path": "src/real_command_runner.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"build.h\"\n#include \"jobserver.h\"\n#include \"limits.h\"\n#include \"subprocess.h\"\n\nstruct RealCommandRunner : public CommandRunner {\n  explicit RealCommandRunner(const BuildConfig& config,\n                             Jobserver::Client* jobserver)\n      : config_(config), jobserver_(jobserver) {}\n  size_t CanRunMore() const override;\n  bool StartCommand(Edge* edge) override;\n  bool WaitForCommand(Result* result) override;\n  std::vector<Edge*> GetActiveEdges() override;\n  void Abort() override;\n\n  void ClearJobTokens() {\n    if (jobserver_) {\n      for (Edge* edge : GetActiveEdges()) {\n        jobserver_->Release(std::move(edge->job_slot_));\n      }\n    }\n  }\n\n  const BuildConfig& config_;\n  SubprocessSet subprocs_;\n  Jobserver::Client* jobserver_ = nullptr;\n  std::map<const Subprocess*, Edge*> subproc_to_edge_;\n};\n\nstd::vector<Edge*> RealCommandRunner::GetActiveEdges() {\n  std::vector<Edge*> edges;\n  for (std::map<const Subprocess*, Edge*>::iterator e =\n           subproc_to_edge_.begin();\n       e != subproc_to_edge_.end(); ++e)\n    edges.push_back(e->second);\n  return edges;\n}\n\nvoid RealCommandRunner::Abort() {\n  ClearJobTokens();\n  subprocs_.Clear();\n}\n\nsize_t RealCommandRunner::CanRunMore() const {\n  size_t subproc_number =\n      subprocs_.running_.size() + subprocs_.finished_.size();\n\n  int64_t capacity = config_.parallelism - subproc_number;\n\n  if (jobserver_) {\n    // When a jobserver token pool is used, make the\n    // capacity infinite, and let FindWork() limit jobs\n    // through token acquisitions instead.\n    capacity = INT_MAX;\n  }\n\n  if (config_.max_load_average > 0.0f) {\n    int load_capacity = config_.max_load_average - GetLoadAverage();\n    if (load_capacity < capacity)\n      capacity = load_capacity;\n  }\n\n  if (capacity < 0)\n    capacity = 0;\n\n  if (capacity == 0 && subprocs_.running_.empty())\n    // Ensure that we make progress.\n    capacity = 1;\n\n  return capacity;\n}\n\nbool RealCommandRunner::StartCommand(Edge* edge) {\n  std::string command = edge->EvaluateCommand();\n  Subprocess* subproc = subprocs_.Add(command, edge->use_console());\n  if (!subproc)\n    return false;\n  subproc_to_edge_.insert(std::make_pair(subproc, edge));\n\n  return true;\n}\n\nbool RealCommandRunner::WaitForCommand(Result* result) {\n  Subprocess* subproc;\n  while ((subproc = subprocs_.NextFinished()) == NULL) {\n    bool interrupted = subprocs_.DoWork();\n    if (interrupted) {\n      result->status = ExitInterrupted;\n      return false;\n    }\n  }\n\n  result->status = subproc->Finish();\n  result->output = subproc->GetOutput();\n\n  std::map<const Subprocess*, Edge*>::iterator e =\n      subproc_to_edge_.find(subproc);\n  result->edge = e->second;\n  subproc_to_edge_.erase(e);\n\n  delete subproc;\n  return true;\n}\n\nCommandRunner* CommandRunner::factory(const BuildConfig& config,\n                                      Jobserver::Client* jobserver) {\n  return new RealCommandRunner(config, jobserver);\n}\n"
  },
  {
    "path": "src/state.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"state.h\"\n\n#include <assert.h>\n#include <stdio.h>\n\n#include \"edit_distance.h\"\n#include \"graph.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nvoid Pool::EdgeScheduled(const Edge& edge) {\n  if (depth_ != 0)\n    current_use_ += edge.weight();\n}\n\nvoid Pool::EdgeFinished(const Edge& edge) {\n  if (depth_ != 0)\n    current_use_ -= edge.weight();\n}\n\nvoid Pool::DelayEdge(Edge* edge) {\n  assert(depth_ != 0);\n  delayed_.insert(edge);\n}\n\nvoid Pool::RetrieveReadyEdges(EdgePriorityQueue* ready_queue) {\n  DelayedEdges::iterator it = delayed_.begin();\n  while (it != delayed_.end()) {\n    Edge* edge = *it;\n    if (current_use_ + edge->weight() > depth_)\n      break;\n    ready_queue->push(edge);\n    EdgeScheduled(*edge);\n    ++it;\n  }\n  delayed_.erase(delayed_.begin(), it);\n}\n\nvoid Pool::Dump() const {\n  printf(\"%s (%d/%d) ->\\n\", name_.c_str(), current_use_, depth_);\n  for (DelayedEdges::const_iterator it = delayed_.begin();\n       it != delayed_.end(); ++it)\n  {\n    printf(\"\\t\");\n    (*it)->Dump();\n  }\n}\n\nPool State::kDefaultPool(\"\", 0);\nPool State::kConsolePool(\"console\", 1);\n\nState::State() {\n  bindings_.AddRule(Rule::Phony());\n  AddPool(&kDefaultPool);\n  AddPool(&kConsolePool);\n}\n\nvoid State::AddPool(Pool* pool) {\n  assert(LookupPool(pool->name()) == NULL);\n  pools_[pool->name()] = pool;\n}\n\nPool* State::LookupPool(const string& pool_name) {\n  map<string, Pool*>::iterator i = pools_.find(pool_name);\n  if (i == pools_.end())\n    return NULL;\n  return i->second;\n}\n\nEdge* State::AddEdge(const Rule* rule) {\n  Edge* edge = new Edge();\n  edge->rule_ = rule;\n  edge->pool_ = &State::kDefaultPool;\n  edge->env_ = &bindings_;\n  edge->id_ = edges_.size();\n  edges_.push_back(edge);\n  return edge;\n}\n\nNode* State::GetNode(StringPiece path, uint64_t slash_bits) {\n  Node* node = LookupNode(path);\n  if (node)\n    return node;\n  node = new Node(path.AsString(), slash_bits);\n  paths_[node->path()] = node;\n  return node;\n}\n\nNode* State::LookupNode(StringPiece path) const {\n  Paths::const_iterator i = paths_.find(path);\n  if (i != paths_.end())\n    return i->second;\n  return NULL;\n}\n\nNode* State::SpellcheckNode(const string& path) {\n  const bool kAllowReplacements = true;\n  const int kMaxValidEditDistance = 3;\n\n  int min_distance = kMaxValidEditDistance + 1;\n  Node* result = NULL;\n  for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {\n    int distance = EditDistance(\n        i->first, path, kAllowReplacements, kMaxValidEditDistance);\n    if (distance < min_distance && i->second) {\n      min_distance = distance;\n      result = i->second;\n    }\n  }\n  return result;\n}\n\nvoid State::AddIn(Edge* edge, StringPiece path, uint64_t slash_bits) {\n  Node* node = GetNode(path, slash_bits);\n  node->set_generated_by_dep_loader(false);\n  edge->inputs_.push_back(node);\n  node->AddOutEdge(edge);\n}\n\nbool State::AddOut(Edge* edge, StringPiece path, uint64_t slash_bits,\n                   std::string* err) {\n  Node* node = GetNode(path, slash_bits);\n  if (Edge* other = node->in_edge()) {\n    if (other == edge) {\n      *err = path.AsString() + \" is defined as an output multiple times\";\n    } else {\n      *err = \"multiple rules generate \" + path.AsString();\n    }\n    return false;\n  }\n  edge->outputs_.push_back(node);\n  node->set_in_edge(edge);\n  node->set_generated_by_dep_loader(false);\n  return true;\n}\n\nvoid State::AddValidation(Edge* edge, StringPiece path, uint64_t slash_bits) {\n  Node* node = GetNode(path, slash_bits);\n  edge->validations_.push_back(node);\n  node->AddValidationOutEdge(edge);\n  node->set_generated_by_dep_loader(false);\n}\n\nbool State::AddDefault(StringPiece path, string* err) {\n  Node* node = LookupNode(path);\n  if (!node) {\n    *err = \"unknown target '\" + path.AsString() + \"'\";\n    return false;\n  }\n  defaults_.push_back(node);\n  return true;\n}\n\nvector<Node*> State::RootNodes(string* err) const {\n  vector<Node*> root_nodes;\n  // Search for nodes with no output.\n  for (vector<Edge*>::const_iterator e = edges_.begin();\n       e != edges_.end(); ++e) {\n    for (vector<Node*>::const_iterator out = (*e)->outputs_.begin();\n         out != (*e)->outputs_.end(); ++out) {\n      if ((*out)->out_edges().empty())\n        root_nodes.push_back(*out);\n    }\n  }\n\n  if (!edges_.empty() && root_nodes.empty())\n    *err = \"could not determine root nodes of build graph\";\n\n  return root_nodes;\n}\n\nvector<Node*> State::DefaultNodes(string* err) const {\n  return defaults_.empty() ? RootNodes(err) : defaults_;\n}\n\nvoid State::Reset() {\n  for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i)\n    i->second->ResetState();\n  for (vector<Edge*>::iterator e = edges_.begin(); e != edges_.end(); ++e) {\n    (*e)->outputs_ready_ = false;\n    (*e)->deps_loaded_ = false;\n    (*e)->mark_ = Edge::VisitNone;\n  }\n}\n\nvoid State::Dump() {\n  for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {\n    Node* node = i->second;\n    printf(\"%s %s [id:%d]\\n\",\n           node->path().c_str(),\n           node->status_known() ? (node->dirty() ? \"dirty\" : \"clean\")\n                                : \"unknown\",\n           node->id());\n  }\n  if (!pools_.empty()) {\n    printf(\"resource_pools:\\n\");\n    for (map<string, Pool*>::const_iterator it = pools_.begin();\n         it != pools_.end(); ++it)\n    {\n      if (!it->second->name().empty()) {\n        it->second->Dump();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/state.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_STATE_H_\n#define NINJA_STATE_H_\n\n#include <map>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"eval_env.h\"\n#include \"graph.h\"\n#include \"hash_map.h\"\n#include \"util.h\"\n\nstruct Edge;\nstruct Node;\nstruct Rule;\n\n/// A pool for delayed edges.\n/// Pools are scoped to a State. Edges within a State will share Pools. A Pool\n/// will keep a count of the total 'weight' of the currently scheduled edges. If\n/// a Plan attempts to schedule an Edge which would cause the total weight to\n/// exceed the depth of the Pool, the Pool will enqueue the Edge instead of\n/// allowing the Plan to schedule it. The Pool will relinquish queued Edges when\n/// the total scheduled weight diminishes enough (i.e. when a scheduled edge\n/// completes).\nstruct Pool {\n  Pool(const std::string& name, int depth)\n    : name_(name), current_use_(0), depth_(depth), delayed_() {}\n\n  // A depth of 0 is infinite\n  bool is_valid() const { return depth_ >= 0; }\n  int depth() const { return depth_; }\n  const std::string& name() const { return name_; }\n  int current_use() const { return current_use_; }\n\n  /// true if the Pool might delay this edge\n  bool ShouldDelayEdge() const { return depth_ != 0; }\n\n  /// informs this Pool that the given edge is committed to be run.\n  /// Pool will count this edge as using resources from this pool.\n  void EdgeScheduled(const Edge& edge);\n\n  /// informs this Pool that the given edge is no longer runnable, and should\n  /// relinquish its resources back to the pool\n  void EdgeFinished(const Edge& edge);\n\n  /// adds the given edge to this Pool to be delayed.\n  void DelayEdge(Edge* edge);\n\n  /// Pool will add zero or more edges to the ready_queue\n  void RetrieveReadyEdges(EdgePriorityQueue* ready_queue);\n\n  /// Dump the Pool and its edges (useful for debugging).\n  void Dump() const;\n\n private:\n  std::string name_;\n\n  /// |current_use_| is the total of the weights of the edges which are\n  /// currently scheduled in the Plan (i.e. the edges in Plan::ready_).\n  int current_use_;\n  int depth_;\n\n  struct WeightedEdgeCmp {\n    bool operator()(const Edge* a, const Edge* b) const {\n      if (!a) return b;\n      if (!b) return false;\n      int weight_diff = a->weight() - b->weight();\n      if (weight_diff != 0) {\n        return weight_diff < 0;\n      }\n      return EdgePriorityGreater()(a, b);\n    }\n  };\n\n  typedef std::set<Edge*, WeightedEdgeCmp> DelayedEdges;\n  DelayedEdges delayed_;\n};\n\n/// Global state (file status) for a single run.\nstruct State {\n  static Pool kDefaultPool;\n  static Pool kConsolePool;\n\n  State();\n\n  void AddPool(Pool* pool);\n  Pool* LookupPool(const std::string& pool_name);\n\n  Edge* AddEdge(const Rule* rule);\n\n  Node* GetNode(StringPiece path, uint64_t slash_bits);\n  Node* LookupNode(StringPiece path) const;\n  Node* SpellcheckNode(const std::string& path);\n\n  /// Add input / output / validation nodes to a given edge. This also\n  /// ensures that the generated_by_dep_loader() flag for all these nodes\n  /// is set to false, to indicate that they come from the input manifest.\n  void AddIn(Edge* edge, StringPiece path, uint64_t slash_bits);\n  bool AddOut(Edge* edge, StringPiece path, uint64_t slash_bits, std::string* err);\n  void AddValidation(Edge* edge, StringPiece path, uint64_t slash_bits);\n  bool AddDefault(StringPiece path, std::string* error);\n\n  /// Reset state.  Keeps all nodes and edges, but restores them to the\n  /// state where we haven't yet examined the disk for dirty state.\n  void Reset();\n\n  /// Dump the nodes and Pools (useful for debugging).\n  void Dump();\n\n  /// @return the root node(s) of the graph. (Root nodes have no output edges).\n  /// @param error where to write the error message if somethings went wrong.\n  std::vector<Node*> RootNodes(std::string* error) const;\n  std::vector<Node*> DefaultNodes(std::string* error) const;\n\n  /// Mapping of path -> Node.\n  typedef ExternalStringHashMap<Node*>::Type Paths;\n  Paths paths_;\n\n  /// All the pools used in the graph.\n  std::map<std::string, Pool*> pools_;\n\n  /// All the edges of the graph.\n  std::vector<Edge*> edges_;\n\n  BindingEnv bindings_;\n  std::vector<Node*> defaults_;\n};\n\n#endif  // NINJA_STATE_H_\n"
  },
  {
    "path": "src/state_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"graph.h\"\n#include \"state.h\"\n#include \"test.h\"\n\nusing namespace std;\n\nnamespace {\n\nTEST(State, Basic) {\n  State state;\n\n  EvalString command;\n  command.AddText(\"cat \");\n  command.AddSpecial(\"in\");\n  command.AddText(\" > \");\n  command.AddSpecial(\"out\");\n\n  Rule* rule = new Rule(\"cat\");\n  rule->AddBinding(\"command\", command);\n  state.bindings_.AddRule(std::unique_ptr<Rule>(rule));\n\n  Edge* edge = state.AddEdge(rule);\n  state.AddIn(edge, \"in1\", 0);\n  state.AddIn(edge, \"in2\", 0);\n  state.AddOut(edge, \"out\", 0, nullptr);\n\n  EXPECT_EQ(\"cat in1 in2 > out\", edge->EvaluateCommand());\n\n  EXPECT_FALSE(state.GetNode(\"in1\", 0)->dirty());\n  EXPECT_FALSE(state.GetNode(\"in2\", 0)->dirty());\n  EXPECT_FALSE(state.GetNode(\"out\", 0)->dirty());\n}\n\n}  // namespace\n"
  },
  {
    "path": "src/status.h",
    "content": "// Copyright 2016 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_STATUS_H_\n#define NINJA_STATUS_H_\n\n#include <string>\n#include \"exit_status.h\"\n\nstruct BuildConfig;\nstruct Edge;\nstruct Explanations;\n\n/// Abstract interface to object that tracks the status of a build:\n/// completion fraction, printing updates.\nstruct Status {\n  virtual void EdgeAddedToPlan(const Edge* edge) = 0;\n  virtual void EdgeRemovedFromPlan(const Edge* edge) = 0;\n  virtual void BuildEdgeStarted(const Edge* edge,\n                                int64_t start_time_millis) = 0;\n  virtual void BuildEdgeFinished(Edge* edge, int64_t start_time_millis,\n                                 int64_t end_time_millis, ExitStatus exit_code,\n                                 const std::string& output) = 0;\n  virtual void BuildStarted() = 0;\n  virtual void BuildFinished() = 0;\n\n  /// Set the Explanations instance to use to report explanations,\n  /// argument can be nullptr if no explanations need to be printed\n  /// (which is the default).\n  virtual void SetExplanations(Explanations*) = 0;\n\n  virtual void Info(const char* msg, ...) = 0;\n  virtual void Warning(const char* msg, ...) = 0;\n  virtual void Error(const char* msg, ...) = 0;\n\n  virtual ~Status() { }\n\n  /// creates the actual implementation\n  static Status* factory(const BuildConfig&);\n};\n\n#endif // NINJA_STATUS_H_\n"
  },
  {
    "path": "src/status_printer.cc",
    "content": "// Copyright 2016 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"status_printer.h\"\n\n#ifdef _WIN32\n#include \"win32port.h\"\n#else\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n#include <cinttypes>\n#endif\n\n#include <stdarg.h>\n#include <stdlib.h>\n\n#ifdef _WIN32\n#include <fcntl.h>\n#include <io.h>\n#endif\n\n#include \"build.h\"\n#include \"debug_flags.h\"\n#include \"exit_status.h\"\n\nusing namespace std;\n\nStatus* Status::factory(const BuildConfig& config) {\n  return new StatusPrinter(config);\n}\n\nStatusPrinter::StatusPrinter(const BuildConfig& config)\n    : config_(config), started_edges_(0), finished_edges_(0), total_edges_(0),\n      running_edges_(0), progress_status_format_(NULL),\n      current_rate_(config.parallelism) {\n  // Don't do anything fancy in verbose mode.\n  if (config_.verbosity != BuildConfig::NORMAL)\n    printer_.set_smart_terminal(false);\n\n  progress_status_format_ = getenv(\"NINJA_STATUS\");\n  if (!progress_status_format_)\n    progress_status_format_ = \"[%f/%t] \";\n}\n\nvoid StatusPrinter::EdgeAddedToPlan(const Edge* edge) {\n  ++total_edges_;\n\n  // Do we know how long did this edge take last time?\n  if (edge->prev_elapsed_time_millis != -1) {\n    ++eta_predictable_edges_total_;\n    ++eta_predictable_edges_remaining_;\n    eta_predictable_cpu_time_total_millis_ += edge->prev_elapsed_time_millis;\n    eta_predictable_cpu_time_remaining_millis_ +=\n        edge->prev_elapsed_time_millis;\n  } else\n    ++eta_unpredictable_edges_remaining_;\n}\n\nvoid StatusPrinter::EdgeRemovedFromPlan(const Edge* edge) {\n  --total_edges_;\n\n  // Do we know how long did this edge take last time?\n  if (edge->prev_elapsed_time_millis != -1) {\n    --eta_predictable_edges_total_;\n    --eta_predictable_edges_remaining_;\n    eta_predictable_cpu_time_total_millis_ -= edge->prev_elapsed_time_millis;\n    eta_predictable_cpu_time_remaining_millis_ -=\n        edge->prev_elapsed_time_millis;\n  } else\n    --eta_unpredictable_edges_remaining_;\n}\n\nvoid StatusPrinter::BuildEdgeStarted(const Edge* edge,\n                                     int64_t start_time_millis) {\n  ++started_edges_;\n  ++running_edges_;\n  time_millis_ = start_time_millis;\n\n  if (edge->use_console() || printer_.is_smart_terminal())\n    PrintStatus(edge, start_time_millis);\n\n  if (edge->use_console())\n    printer_.SetConsoleLocked(true);\n}\n\nvoid StatusPrinter::RecalculateProgressPrediction() {\n  time_predicted_percentage_ = 0.0;\n\n  // Sometimes, the previous and actual times may be wildly different.\n  // For example, the previous build may have been fully recovered from ccache,\n  // so it was blazing fast, while the new build no longer gets hits from ccache\n  // for whatever reason, so it actually compiles code, which takes much longer.\n  // We should detect such cases, and avoid using \"wrong\" previous times.\n\n  // Note that we will only use the previous times if there are edges with\n  // previous time knowledge remaining.\n  bool use_previous_times = eta_predictable_edges_remaining_ &&\n                            eta_predictable_cpu_time_remaining_millis_;\n\n  // Iff we have sufficient statistical information for the current run,\n  // that is, if we have took at least 15 sec AND finished at least 5% of edges,\n  // we can check whether our performance so far matches the previous one.\n  if (use_previous_times && total_edges_ && finished_edges_ &&\n      (time_millis_ >= 15 * 1e3) &&\n      (((double)finished_edges_ / total_edges_) >= 0.05)) {\n    // Over the edges we've just run, how long did they take on average?\n    double actual_average_cpu_time_millis =\n        (double)cpu_time_millis_ / finished_edges_;\n    // What is the previous average, for the edges with such knowledge?\n    double previous_average_cpu_time_millis =\n        (double)eta_predictable_cpu_time_total_millis_ /\n        eta_predictable_edges_total_;\n\n    double ratio = std::max(previous_average_cpu_time_millis,\n                            actual_average_cpu_time_millis) /\n                   std::min(previous_average_cpu_time_millis,\n                            actual_average_cpu_time_millis);\n\n    // Let's say that the average times should differ by less than 10x\n    use_previous_times = ratio < 10;\n  }\n\n  int edges_with_known_runtime = finished_edges_;\n  if (use_previous_times)\n    edges_with_known_runtime += eta_predictable_edges_remaining_;\n  if (edges_with_known_runtime == 0)\n    return;\n\n  int edges_with_unknown_runtime = use_previous_times\n                                       ? eta_unpredictable_edges_remaining_\n                                       : (total_edges_ - finished_edges_);\n\n  // Given the time elapsed on the edges we've just run,\n  // and the runtime of the edges for which we know previous runtime,\n  // what's the edge's average runtime?\n  int64_t edges_known_runtime_total_millis = cpu_time_millis_;\n  if (use_previous_times)\n    edges_known_runtime_total_millis +=\n        eta_predictable_cpu_time_remaining_millis_;\n\n  double average_cpu_time_millis =\n      (double)edges_known_runtime_total_millis / edges_with_known_runtime;\n\n  // For the edges for which we do not have the previous runtime,\n  // let's assume that their average runtime is the same as for the other edges,\n  // and we therefore can predict their remaining runtime.\n  double unpredictable_cpu_time_remaining_millis =\n      average_cpu_time_millis * edges_with_unknown_runtime;\n\n  // And therefore we can predict the remaining and total runtimes.\n  double total_cpu_time_remaining_millis =\n      unpredictable_cpu_time_remaining_millis;\n  if (use_previous_times)\n    total_cpu_time_remaining_millis +=\n        eta_predictable_cpu_time_remaining_millis_;\n  double total_cpu_time_millis =\n      cpu_time_millis_ + total_cpu_time_remaining_millis;\n  if (total_cpu_time_millis == 0.0)\n    return;\n\n  // After that we can tell how much work we've completed, in time units.\n  time_predicted_percentage_ = cpu_time_millis_ / total_cpu_time_millis;\n}\n\nvoid StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t start_time_millis,\n                                      int64_t end_time_millis, ExitStatus exit_code,\n                                      const string& output) {\n  time_millis_ = end_time_millis;\n  ++finished_edges_;\n\n  int64_t elapsed = end_time_millis - start_time_millis;\n  cpu_time_millis_ += elapsed;\n\n  // Do we know how long did this edge take last time?\n  if (edge->prev_elapsed_time_millis != -1) {\n    --eta_predictable_edges_remaining_;\n    eta_predictable_cpu_time_remaining_millis_ -=\n        edge->prev_elapsed_time_millis;\n  } else\n    --eta_unpredictable_edges_remaining_;\n\n  if (edge->use_console())\n    printer_.SetConsoleLocked(false);\n\n  if (config_.verbosity == BuildConfig::QUIET)\n    return;\n\n  if (!edge->use_console())\n    PrintStatus(edge, end_time_millis);\n\n  --running_edges_;\n\n  // Print the command that is spewing before printing its output.\n  if (exit_code != ExitSuccess) {\n    string outputs;\n    for (vector<Node*>::const_iterator o = edge->outputs_.begin();\n         o != edge->outputs_.end(); ++o)\n      outputs += (*o)->path() + \" \";\n\n    string failed = \"FAILED: [code=\" + std::to_string(exit_code) + \"] \";\n    if (printer_.supports_color()) {\n        printer_.PrintOnNewLine(\"\\x1B[31m\" + failed + \"\\x1B[0m\" + outputs + \"\\n\");\n    } else {\n        printer_.PrintOnNewLine(failed + outputs + \"\\n\");\n    }\n    printer_.PrintOnNewLine(edge->EvaluateCommand() + \"\\n\");\n  }\n\n  if (!output.empty()) {\n#ifdef _WIN32\n    // Fix extra CR being added on Windows, writing out CR CR LF (#773)\n    fflush(stdout);  // Begin Windows extra CR fix\n    _setmode(_fileno(stdout), _O_BINARY);\n#endif\n\n    // ninja sets stdout and stderr of subprocesses to a pipe, to be able to\n    // check if the output is empty. Some compilers, e.g. clang, check\n    // isatty(stderr) to decide if they should print colored output.\n    // To make it possible to use colored output with ninja, subprocesses should\n    // be run with a flag that forces them to always print color escape codes.\n    // To make sure these escape codes don't show up in a file if ninja's output\n    // is piped to a file, ninja strips ansi escape codes again if it's not\n    // writing to a |smart_terminal_|.\n    // (Launching subprocesses in pseudo ttys doesn't work because there are\n    // only a few hundred available on some systems, and ninja can launch\n    // thousands of parallel compile commands.)\n    if (printer_.supports_color() || output.find('\\x1b') == std::string::npos) {\n      printer_.PrintOnNewLine(output);\n    } else {\n      std::string final_output = StripAnsiEscapeCodes(output);\n      printer_.PrintOnNewLine(final_output);\n    }\n\n#ifdef _WIN32\n    fflush(stdout);\n    _setmode(_fileno(stdout), _O_TEXT);  // End Windows extra CR fix\n#endif\n  }\n}\n\nvoid StatusPrinter::BuildStarted() {\n  started_edges_ = 0;\n  finished_edges_ = 0;\n  running_edges_ = 0;\n}\n\nvoid StatusPrinter::BuildFinished() {\n  printer_.SetConsoleLocked(false);\n  printer_.PrintOnNewLine(\"\");\n}\n\nstring StatusPrinter::FormatProgressStatus(const char* progress_status_format,\n                                           int64_t time_millis) const {\n  string out;\n  char buf[32];\n  for (const char* s = progress_status_format; *s != '\\0'; ++s) {\n    if (*s == '%') {\n      ++s;\n      switch (*s) {\n      case '%':\n        out.push_back('%');\n        break;\n\n        // Started edges.\n      case 's':\n        snprintf(buf, sizeof(buf), \"%d\", started_edges_);\n        out += buf;\n        break;\n\n        // Total edges.\n      case 't':\n        snprintf(buf, sizeof(buf), \"%d\", total_edges_);\n        out += buf;\n        break;\n\n        // Running edges.\n      case 'r': {\n        snprintf(buf, sizeof(buf), \"%d\", running_edges_);\n        out += buf;\n        break;\n      }\n\n        // Unstarted edges.\n      case 'u':\n        snprintf(buf, sizeof(buf), \"%d\", total_edges_ - started_edges_);\n        out += buf;\n        break;\n\n        // Finished edges.\n      case 'f':\n        snprintf(buf, sizeof(buf), \"%d\", finished_edges_);\n        out += buf;\n        break;\n\n        // Overall finished edges per second.\n      case 'o':\n        SnprintfRate(finished_edges_ / (time_millis_ / 1e3), buf, \"%.1f\");\n        out += buf;\n        break;\n\n        // Current rate, average over the last '-j' jobs.\n      case 'c':\n        current_rate_.UpdateRate(finished_edges_, time_millis_);\n        SnprintfRate(current_rate_.rate(), buf, \"%.1f\");\n        out += buf;\n        break;\n\n        // Percentage of edges completed\n      case 'p': {\n        int percent = 0;\n        if (finished_edges_ != 0 && total_edges_ != 0)\n          percent = (100 * finished_edges_) / total_edges_;\n        snprintf(buf, sizeof(buf), \"%3i%%\", percent);\n        out += buf;\n        break;\n      }\n\n#define FORMAT_TIME_HMMSS(t)                                                \\\n  \"%\" PRId64 \":%02\" PRId64 \":%02\" PRId64 \"\", (t) / 3600, ((t) % 3600) / 60, \\\n      (t) % 60\n#define FORMAT_TIME_MMSS(t) \"%02\" PRId64 \":%02\" PRId64 \"\", (t) / 60, (t) % 60\n\n        // Wall time\n      case 'e':  // elapsed, seconds\n      case 'w':  // elapsed, human-readable\n      case 'E':  // ETA, seconds\n      case 'W':  // ETA, human-readable\n      {\n        double elapsed_sec = time_millis_ / 1e3;\n        double eta_sec = -1;  // To be printed as \"?\".\n        if (time_predicted_percentage_ != 0.0) {\n          // So, we know that we've spent time_millis_ wall clock,\n          // and that is time_predicted_percentage_ percent.\n          // How much time will we need to complete 100%?\n          double total_wall_time = time_millis_ / time_predicted_percentage_;\n          // Naturally, that gives us the time remaining.\n          eta_sec = (total_wall_time - time_millis_) / 1e3;\n        }\n\n        const bool print_with_hours =\n            elapsed_sec >= 60 * 60 || eta_sec >= 60 * 60;\n\n        double sec = -1;\n        switch (*s) {\n        case 'e':  // elapsed, seconds\n        case 'w':  // elapsed, human-readable\n          sec = elapsed_sec;\n          break;\n        case 'E':  // ETA, seconds\n        case 'W':  // ETA, human-readable\n          sec = eta_sec;\n          break;\n        }\n\n        if (sec < 0)\n          snprintf(buf, sizeof(buf), \"?\");\n        else {\n          switch (*s) {\n          case 'e':  // elapsed, seconds\n          case 'E':  // ETA, seconds\n            snprintf(buf, sizeof(buf), \"%.3f\", sec);\n            break;\n          case 'w':  // elapsed, human-readable\n          case 'W':  // ETA, human-readable\n            if (print_with_hours)\n              snprintf(buf, sizeof(buf), FORMAT_TIME_HMMSS((int64_t)sec));\n            else\n              snprintf(buf, sizeof(buf), FORMAT_TIME_MMSS((int64_t)sec));\n            break;\n          }\n        }\n        out += buf;\n        break;\n      }\n\n      // Percentage of time spent out of the predicted time total\n      case 'P': {\n        snprintf(buf, sizeof(buf), \"%3i%%\",\n                 (int)(100. * time_predicted_percentage_));\n        out += buf;\n        break;\n      }\n\n      default:\n        Fatal(\"unknown placeholder '%%%c' in $NINJA_STATUS\", *s);\n        return \"\";\n      }\n    } else {\n      out.push_back(*s);\n    }\n  }\n\n  return out;\n}\n\nvoid StatusPrinter::PrintStatus(const Edge* edge, int64_t time_millis) {\n  if (explanations_) {\n    // Collect all explanations for the current edge's outputs.\n    std::vector<std::string> explanations;\n    for (Node* output : edge->outputs_) {\n      explanations_->LookupAndAppend(output, &explanations);\n    }\n    if (!explanations.empty()) {\n      // Start a new line so that the first explanation does not append to the\n      // status line.\n      printer_.PrintOnNewLine(\"\");\n      for (const auto& exp : explanations) {\n        fprintf(stderr, \"ninja explain: %s\\n\", exp.c_str());\n      }\n    }\n  }\n\n  if (config_.verbosity == BuildConfig::QUIET\n      || config_.verbosity == BuildConfig::NO_STATUS_UPDATE)\n    return;\n\n  RecalculateProgressPrediction();\n\n  bool force_full_command = config_.verbosity == BuildConfig::VERBOSE;\n\n  string to_print = edge->GetBinding(\"description\");\n  if (to_print.empty() || force_full_command)\n    to_print = edge->GetBinding(\"command\");\n\n  to_print = FormatProgressStatus(progress_status_format_, time_millis)\n      + to_print;\n\n  printer_.Print(to_print,\n                 force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE);\n}\n\nvoid StatusPrinter::Warning(const char* msg, ...) {\n  va_list ap;\n  va_start(ap, msg);\n  ::Warning(msg, ap);\n  va_end(ap);\n}\n\nvoid StatusPrinter::Error(const char* msg, ...) {\n  va_list ap;\n  va_start(ap, msg);\n  ::Error(msg, ap);\n  va_end(ap);\n}\n\nvoid StatusPrinter::Info(const char* msg, ...) {\n  va_list ap;\n  va_start(ap, msg);\n  ::Info(msg, ap);\n  va_end(ap);\n}\n"
  },
  {
    "path": "src/status_printer.h",
    "content": "// Copyright 2016 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n#pragma once\n\n#include <cstdint>\n#include <queue>\n\n#include \"exit_status.h\"\n#include \"explanations.h\"\n#include \"line_printer.h\"\n#include \"status.h\"\n\n/// Implementation of the Status interface that prints the status as\n/// human-readable strings to stdout\nstruct StatusPrinter : Status {\n  explicit StatusPrinter(const BuildConfig& config);\n\n  /// Callbacks for the Plan to notify us about adding/removing Edge's.\n  void EdgeAddedToPlan(const Edge* edge) override;\n  void EdgeRemovedFromPlan(const Edge* edge) override;\n\n  void BuildEdgeStarted(const Edge* edge, int64_t start_time_millis) override;\n  void BuildEdgeFinished(Edge* edge, int64_t start_time_millis,\n                                 int64_t end_time_millis, ExitStatus exit_code,\n                                 const std::string& output) override;\n  void BuildStarted() override;\n  void BuildFinished() override;\n\n  void Info(const char* msg, ...) override;\n  void Warning(const char* msg, ...) override;\n  void Error(const char* msg, ...) override;\n\n  /// Format the progress status string by replacing the placeholders.\n  /// See the user manual for more information about the available\n  /// placeholders.\n  /// @param progress_status_format The format of the progress status.\n  /// @param status The status of the edge.\n  std::string FormatProgressStatus(const char* progress_status_format,\n                                   int64_t time_millis) const;\n\n  /// Set the |explanations_| pointer. Used to implement `-d explain`.\n  void SetExplanations(Explanations* explanations) override {\n    explanations_ = explanations;\n  }\n\n private:\n  void PrintStatus(const Edge* edge, int64_t time_millis);\n\n  const BuildConfig& config_;\n\n  int started_edges_, finished_edges_, total_edges_, running_edges_;\n\n  /// How much wall clock elapsed so far?\n  int64_t time_millis_ = 0;\n\n  /// How much cpu clock elapsed so far?\n  int64_t cpu_time_millis_ = 0;\n\n  /// What percentage of predicted total time have elapsed already?\n  double time_predicted_percentage_ = 0.0;\n\n  /// Out of all the edges, for how many do we know previous time?\n  int eta_predictable_edges_total_ = 0;\n  /// And how much time did they all take?\n  int64_t eta_predictable_cpu_time_total_millis_ = 0;\n\n  /// Out of all the non-finished edges, for how many do we know previous time?\n  int eta_predictable_edges_remaining_ = 0;\n  /// And how much time will they all take?\n  int64_t eta_predictable_cpu_time_remaining_millis_ = 0;\n\n  /// For how many edges we don't know the previous run time?\n  int eta_unpredictable_edges_remaining_ = 0;\n\n  void RecalculateProgressPrediction();\n\n  /// Prints progress output.\n  LinePrinter printer_;\n\n  /// An optional Explanations pointer, used to implement `-d explain`.\n  Explanations* explanations_ = nullptr;\n\n  /// The custom progress status format to use.\n  const char* progress_status_format_;\n\n  template <size_t S>\n  void SnprintfRate(double rate, char (&buf)[S], const char* format) const {\n    if (rate == -1)\n      snprintf(buf, S, \"?\");\n    else\n      snprintf(buf, S, format, rate);\n  }\n\n  struct SlidingRateInfo {\n    SlidingRateInfo(int n) : rate_(-1), N(n), last_update_(-1) {}\n\n    double rate() { return rate_; }\n\n    void UpdateRate(int update_hint, int64_t time_millis) {\n      if (update_hint == last_update_)\n        return;\n      last_update_ = update_hint;\n\n      if (times_.size() == N)\n        times_.pop();\n      times_.push(time_millis);\n      if (times_.back() != times_.front())\n        rate_ = times_.size() / ((times_.back() - times_.front()) / 1e3);\n    }\n\n   private:\n    double rate_;\n    const size_t N;\n    std::queue<double> times_;\n    int last_update_;\n  };\n\n  mutable SlidingRateInfo current_rate_;\n};\n"
  },
  {
    "path": "src/status_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"status.h\"\n\n#include \"test.h\"\n\nTEST(StatusTest, StatusFormatElapsed) {\n  BuildConfig config;\n  StatusPrinter status(config);\n\n  status.BuildStarted();\n  // Before any task is done, the elapsed time must be zero.\n  EXPECT_EQ(\"[%/e0.000]\", status.FormatProgressStatus(\"[%%/e%e]\", 0));\n  // Before any task is done, the elapsed time must be zero.\n  EXPECT_EQ(\"[%/e00:00]\", status.FormatProgressStatus(\"[%%/e%w]\", 0));\n}\n\nTEST(StatusTest, StatusFormatReplacePlaceholder) {\n  BuildConfig config;\n  StatusPrinter status(config);\n\n  EXPECT_EQ(\"[%/s0/t0/r0/u0/f0]\",\n            status.FormatProgressStatus(\"[%%/s%s/t%t/r%r/u%u/f%f]\", 0));\n}\n"
  },
  {
    "path": "src/string_piece.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_STRINGPIECE_H_\n#define NINJA_STRINGPIECE_H_\n\n#include <string>\n\n#include <string.h>\n\n/// StringPiece represents a slice of a string whose memory is managed\n/// externally.  It is useful for reducing the number of std::strings\n/// we need to allocate.\nstruct StringPiece {\n  typedef const char* const_iterator;\n\n  StringPiece() : str_(NULL), len_(0) {}\n\n  /// The constructors intentionally allow for implicit conversions.\n  StringPiece(const std::string& str) : str_(str.data()), len_(str.size()) {}\n  StringPiece(const char* str) : str_(str), len_(strlen(str)) {}\n\n  StringPiece(const char* str, size_t len) : str_(str), len_(len) {}\n\n  bool operator==(const StringPiece& other) const {\n    return len_ == other.len_ && memcmp(str_, other.str_, len_) == 0;\n  }\n\n  bool operator!=(const StringPiece& other) const {\n    return !(*this == other);\n  }\n\n  /// Convert the slice into a full-fledged std::string, copying the\n  /// data into a new string.\n  std::string AsString() const {\n    return len_ ? std::string(str_, len_) : std::string();\n  }\n\n  const_iterator begin() const {\n    return str_;\n  }\n\n  const_iterator end() const {\n    return str_ + len_;\n  }\n\n  char operator[](size_t pos) const {\n    return str_[pos];\n  }\n\n  size_t size() const {\n    return len_;\n  }\n\n  size_t empty() const {\n    return len_ == 0;\n  }\n\n  const char* str_;\n  size_t len_;\n};\n\n#endif  // NINJA_STRINGPIECE_H_\n"
  },
  {
    "path": "src/string_piece_util.cc",
    "content": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"string_piece_util.h\"\n\n#include <algorithm>\n#include <string>\n#include <vector>\nusing namespace std;\n\nvector<StringPiece> SplitStringPiece(StringPiece input, char sep) {\n  vector<StringPiece> elems;\n  elems.reserve(count(input.begin(), input.end(), sep) + 1);\n\n  StringPiece::const_iterator pos = input.begin();\n\n  for (;;) {\n    const char* next_pos = find(pos, input.end(), sep);\n    if (next_pos == input.end()) {\n      elems.push_back(StringPiece(pos, input.end() - pos));\n      break;\n    }\n    elems.push_back(StringPiece(pos, next_pos - pos));\n    pos = next_pos + 1;\n  }\n\n  return elems;\n}\n\nstring JoinStringPiece(const vector<StringPiece>& list, char sep) {\n  if (list.empty()) {\n    return \"\";\n  }\n\n  string ret;\n\n  {\n    size_t cap = list.size() - 1;\n    for (size_t i = 0; i < list.size(); ++i) {\n      cap += list[i].len_;\n    }\n    ret.reserve(cap);\n  }\n\n  for (size_t i = 0; i < list.size(); ++i) {\n    if (i != 0) {\n      ret += sep;\n    }\n    ret.append(list[i].str_, list[i].len_);\n  }\n\n  return ret;\n}\n\nbool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {\n  if (a.len_ != b.len_) {\n    return false;\n  }\n\n  for (size_t i = 0; i < a.len_; ++i) {\n    if (ToLowerASCII(a.str_[i]) != ToLowerASCII(b.str_[i])) {\n      return false;\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "src/string_piece_util.h",
    "content": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_STRINGPIECE_UTIL_H_\n#define NINJA_STRINGPIECE_UTIL_H_\n\n#include <string>\n#include <vector>\n\n#include \"string_piece.h\"\n\nstd::vector<StringPiece> SplitStringPiece(StringPiece input, char sep);\n\nstd::string JoinStringPiece(const std::vector<StringPiece>& list, char sep);\n\ninline char ToLowerASCII(char c) {\n  return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;\n}\n\nbool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b);\n\n#endif  // NINJA_STRINGPIECE_UTIL_H_\n"
  },
  {
    "path": "src/string_piece_util_test.cc",
    "content": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"string_piece_util.h\"\n\n#include \"test.h\"\n\nusing namespace std;\n\nTEST(StringPieceUtilTest, SplitStringPiece) {\n  {\n    string input(\"a:b:c\");\n    vector<StringPiece> list = SplitStringPiece(input, ':');\n\n    EXPECT_EQ(list.size(), size_t(3));\n\n    EXPECT_EQ(list[0], \"a\");\n    EXPECT_EQ(list[1], \"b\");\n    EXPECT_EQ(list[2], \"c\");\n  }\n\n  {\n    string empty;\n    vector<StringPiece> list = SplitStringPiece(empty, ':');\n\n    EXPECT_EQ(list.size(), size_t(1));\n\n    EXPECT_EQ(list[0], \"\");\n  }\n\n  {\n    string one(\"a\");\n    vector<StringPiece> list = SplitStringPiece(one, ':');\n\n    EXPECT_EQ(list.size(), size_t(1));\n\n    EXPECT_EQ(list[0], \"a\");\n  }\n\n  {\n    string sep_only(\":\");\n    vector<StringPiece> list = SplitStringPiece(sep_only, ':');\n\n    EXPECT_EQ(list.size(), size_t(2));\n\n    EXPECT_EQ(list[0], \"\");\n    EXPECT_EQ(list[1], \"\");\n  }\n\n  {\n    string sep(\":a:b:c:\");\n    vector<StringPiece> list = SplitStringPiece(sep, ':');\n\n    EXPECT_EQ(list.size(), size_t(5));\n\n    EXPECT_EQ(list[0], \"\");\n    EXPECT_EQ(list[1], \"a\");\n    EXPECT_EQ(list[2], \"b\");\n    EXPECT_EQ(list[3], \"c\");\n    EXPECT_EQ(list[4], \"\");\n  }\n}\n\nTEST(StringPieceUtilTest, JoinStringPiece) {\n  {\n    string input(\"a:b:c\");\n    vector<StringPiece> list = SplitStringPiece(input, ':');\n\n    EXPECT_EQ(\"a:b:c\", JoinStringPiece(list, ':'));\n    EXPECT_EQ(\"a/b/c\", JoinStringPiece(list, '/'));\n  }\n\n  {\n    string empty;\n    vector<StringPiece> list = SplitStringPiece(empty, ':');\n\n    EXPECT_EQ(\"\", JoinStringPiece(list, ':'));\n  }\n\n  {\n    vector<StringPiece> empty_list;\n\n    EXPECT_EQ(\"\", JoinStringPiece(empty_list, ':'));\n  }\n\n  {\n    string one(\"a\");\n    vector<StringPiece> single_list = SplitStringPiece(one, ':');\n\n    EXPECT_EQ(\"a\", JoinStringPiece(single_list, ':'));\n  }\n\n  {\n    string sep(\":a:b:c:\");\n    vector<StringPiece> list = SplitStringPiece(sep, ':');\n\n    EXPECT_EQ(\":a:b:c:\", JoinStringPiece(list, ':'));\n  }\n}\n\nTEST(StringPieceUtilTest, ToLowerASCII) {\n  EXPECT_EQ('a', ToLowerASCII('A'));\n  EXPECT_EQ('z', ToLowerASCII('Z'));\n  EXPECT_EQ('a', ToLowerASCII('a'));\n  EXPECT_EQ('z', ToLowerASCII('z'));\n  EXPECT_EQ('/', ToLowerASCII('/'));\n  EXPECT_EQ('1', ToLowerASCII('1'));\n}\n\nTEST(StringPieceUtilTest, EqualsCaseInsensitiveASCII) {\n  EXPECT_TRUE(EqualsCaseInsensitiveASCII(\"abc\", \"abc\"));\n  EXPECT_TRUE(EqualsCaseInsensitiveASCII(\"abc\", \"ABC\"));\n  EXPECT_TRUE(EqualsCaseInsensitiveASCII(\"abc\", \"aBc\"));\n  EXPECT_TRUE(EqualsCaseInsensitiveASCII(\"AbC\", \"aBc\"));\n  EXPECT_TRUE(EqualsCaseInsensitiveASCII(\"\", \"\"));\n\n  EXPECT_FALSE(EqualsCaseInsensitiveASCII(\"a\", \"ac\"));\n  EXPECT_FALSE(EqualsCaseInsensitiveASCII(\"/\", \"\\\\\"));\n  EXPECT_FALSE(EqualsCaseInsensitiveASCII(\"1\", \"10\"));\n}\n"
  },
  {
    "path": "src/subprocess-posix.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"exit_status.h\"\n#include \"subprocess.h\"\n\n#include <sys/select.h>\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <spawn.h>\n\n#if defined(USE_PPOLL)\n#include <poll.h>\n#else\n#include <sys/select.h>\n#endif\n\nextern char** environ;\n\n#include \"util.h\"\n\nusing namespace std;\n\nnamespace {\n  ExitStatus ParseExitStatus(int status);\n}\n\nSubprocess::Subprocess(bool use_console) : fd_(-1), pid_(-1),\n                                           use_console_(use_console) {\n}\n\nSubprocess::~Subprocess() {\n  if (fd_ >= 0)\n    close(fd_);\n  // Reap child if forgotten.\n  if (pid_ != -1)\n    Finish();\n}\n\nbool Subprocess::Start(SubprocessSet* set, const string& command) {\n  int subproc_stdout_fd = -1;\n  if (use_console_) {\n    fd_ = -1;\n  } else {\n    int output_pipe[2];\n    if (pipe(output_pipe) < 0)\n      Fatal(\"pipe: %s\", strerror(errno));\n    fd_ = output_pipe[0];\n    subproc_stdout_fd = output_pipe[1];\n#if !defined(USE_PPOLL)\n    // If available, we use ppoll in DoWork(); otherwise we use pselect\n    // and so must avoid overly-large FDs.\n    if (fd_ >= static_cast<int>(FD_SETSIZE))\n      Fatal(\"pipe: %s\", strerror(EMFILE));\n#endif  // !USE_PPOLL\n    SetCloseOnExec(fd_);\n  }\n\n  posix_spawn_file_actions_t action;\n  int err = posix_spawn_file_actions_init(&action);\n  if (err != 0)\n    Fatal(\"posix_spawn_file_actions_init: %s\", strerror(err));\n\n  if (!use_console_) {\n    err = posix_spawn_file_actions_addclose(&action, fd_);\n    if (err != 0)\n      Fatal(\"posix_spawn_file_actions_addclose: %s\", strerror(err));\n  }\n\n  posix_spawnattr_t attr;\n  err = posix_spawnattr_init(&attr);\n  if (err != 0)\n    Fatal(\"posix_spawnattr_init: %s\", strerror(err));\n\n  short flags = 0;\n\n  flags |= POSIX_SPAWN_SETSIGMASK;\n  err = posix_spawnattr_setsigmask(&attr, &set->old_mask_);\n  if (err != 0)\n    Fatal(\"posix_spawnattr_setsigmask: %s\", strerror(err));\n  // Signals which are set to be caught in the calling process image are set to\n  // default action in the new process image, so no explicit\n  // POSIX_SPAWN_SETSIGDEF parameter is needed.\n\n  if (!use_console_) {\n    // Put the child in its own process group, so ctrl-c won't reach it.\n    flags |= POSIX_SPAWN_SETPGROUP;\n    // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default.\n\n    // Open /dev/null over stdin.\n    err = posix_spawn_file_actions_addopen(&action, 0, \"/dev/null\", O_RDONLY,\n          0);\n    if (err != 0) {\n      Fatal(\"posix_spawn_file_actions_addopen: %s\", strerror(err));\n    }\n\n    err = posix_spawn_file_actions_adddup2(&action, subproc_stdout_fd, 1);\n    if (err != 0)\n      Fatal(\"posix_spawn_file_actions_adddup2: %s\", strerror(err));\n    err = posix_spawn_file_actions_adddup2(&action, subproc_stdout_fd, 2);\n    if (err != 0)\n      Fatal(\"posix_spawn_file_actions_adddup2: %s\", strerror(err));\n    err = posix_spawn_file_actions_addclose(&action, subproc_stdout_fd);\n    if (err != 0)\n      Fatal(\"posix_spawn_file_actions_addclose: %s\", strerror(err));\n  }\n\n#ifdef POSIX_SPAWN_USEVFORK\n  flags |= POSIX_SPAWN_USEVFORK;\n#endif\n\n  err = posix_spawnattr_setflags(&attr, flags);\n  if (err != 0)\n    Fatal(\"posix_spawnattr_setflags: %s\", strerror(err));\n\n  const char* spawned_args[] = { \"/bin/sh\", \"-c\", command.c_str(), NULL };\n  err = posix_spawn(&pid_, \"/bin/sh\", &action, &attr,\n        const_cast<char**>(spawned_args), environ);\n  if (err != 0)\n    Fatal(\"posix_spawn: %s\", strerror(err));\n\n  err = posix_spawnattr_destroy(&attr);\n  if (err != 0)\n    Fatal(\"posix_spawnattr_destroy: %s\", strerror(err));\n  err = posix_spawn_file_actions_destroy(&action);\n  if (err != 0)\n    Fatal(\"posix_spawn_file_actions_destroy: %s\", strerror(err));\n\n  if (!use_console_)\n    close(subproc_stdout_fd);\n  return true;\n}\n\nvoid Subprocess::OnPipeReady() {\n  char buf[4 << 10];\n  ssize_t len = read(fd_, buf, sizeof(buf));\n  if (len > 0) {\n    buf_.append(buf, len);\n  } else {\n    if (len < 0)\n      Fatal(\"read: %s\", strerror(errno));\n    close(fd_);\n    fd_ = -1;\n  }\n}\n\n\nbool Subprocess::TryFinish(int waitpid_options) {\n  assert(pid_ != -1);\n  int status, ret;\n  while ((ret = waitpid(pid_, &status, waitpid_options)) < 0) {\n    if (errno != EINTR)\n      Fatal(\"waitpid(%d): %s\", pid_, strerror(errno));\n  }\n  if (ret == 0)\n    return false; // Subprocess is alive (WNOHANG-only).\n  pid_ = -1;\n  exit_status_ = ParseExitStatus(status);\n  return true; // Subprocess has terminated.\n}\n\nExitStatus Subprocess::Finish() {\n  if (pid_ != -1) {\n    TryFinish(0);\n    assert(pid_ == -1);\n  }\n  return exit_status_;\n}\n\nnamespace {\n\nExitStatus ParseExitStatus(int status) {\n#ifdef _AIX\n  if (WIFEXITED(status) && WEXITSTATUS(status) & 0x80) {\n    // Map the shell's exit code used for signal failure (128 + signal) to the\n    // status code expected by AIX WIFSIGNALED and WTERMSIG macros which, unlike\n    // other systems, uses a different bit layout.\n    int signal = WEXITSTATUS(status) & 0x7f;\n    status = (signal << 16) | signal;\n  }\n#endif\n\n  if (WIFEXITED(status)) {\n    // propagate the status transparently\n    return static_cast<ExitStatus>(WEXITSTATUS(status));\n  }\n  if (WIFSIGNALED(status)) {\n    if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM\n        || WTERMSIG(status) == SIGHUP)\n      return ExitInterrupted;\n  }\n  // At this point, we exit with any other signal+128\n  return static_cast<ExitStatus>(status + 128);\n}\n\n} // anonymous namespace\n\nbool Subprocess::Done() const {\n  // Console subprocesses share console with ninja, and we consider them done\n  // when they exit.\n  // For other processes, we consider them done when we have consumed all their\n  // output and closed their associated pipe.\n  return (use_console_ && pid_ == -1) || (!use_console_ && fd_ == -1);\n}\n\nconst string& Subprocess::GetOutput() const {\n  return buf_;\n}\n\nvolatile sig_atomic_t SubprocessSet::interrupted_;\nvolatile sig_atomic_t SubprocessSet::s_sigchld_received;\n\nvoid SubprocessSet::SetInterruptedFlag(int signum) {\n  interrupted_ = signum;\n}\n\nvoid SubprocessSet::SigChldHandler(int signo, siginfo_t* info, void* context) {\n  s_sigchld_received = 1;\n}\n\nvoid SubprocessSet::HandlePendingInterruption() {\n  sigset_t pending;\n  sigemptyset(&pending);\n  if (sigpending(&pending) == -1) {\n    perror(\"ninja: sigpending\");\n    return;\n  }\n  if (sigismember(&pending, SIGINT))\n    interrupted_ = SIGINT;\n  else if (sigismember(&pending, SIGTERM))\n    interrupted_ = SIGTERM;\n  else if (sigismember(&pending, SIGHUP))\n    interrupted_ = SIGHUP;\n}\n\nSubprocessSet::SubprocessSet() {\n  // Block all these signals.\n  // Their handlers will only be enabled during ppoll/pselect().\n  sigset_t set;\n  sigemptyset(&set);\n  sigaddset(&set, SIGINT);\n  sigaddset(&set, SIGTERM);\n  sigaddset(&set, SIGHUP);\n  sigaddset(&set, SIGCHLD);\n  if (sigprocmask(SIG_BLOCK, &set, &old_mask_) < 0)\n    Fatal(\"sigprocmask: %s\", strerror(errno));\n\n  struct sigaction act;\n  memset(&act, 0, sizeof(act));\n  act.sa_handler = SetInterruptedFlag;\n  if (sigaction(SIGINT, &act, &old_int_act_) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n  if (sigaction(SIGTERM, &act, &old_term_act_) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n  if (sigaction(SIGHUP, &act, &old_hup_act_) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n\n  memset(&act, 0, sizeof(act));\n  act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;\n  act.sa_sigaction = SigChldHandler;\n  if (sigaction(SIGCHLD, &act, &old_chld_act_) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n}\n\n// Reaps console processes that have exited and moves them from the running set\n// to the finished set.\nvoid SubprocessSet::CheckConsoleProcessTerminated() {\n  if (!s_sigchld_received)\n    return;\n  for (auto i = running_.begin(); i != running_.end(); ) {\n    if ((*i)->use_console_ && (*i)->TryFinish(WNOHANG)) {\n      finished_.push(*i);\n      i = running_.erase(i);\n    } else {\n      ++i;\n    }\n  }\n}\n\nSubprocessSet::~SubprocessSet() {\n  Clear();\n\n  if (sigaction(SIGINT, &old_int_act_, 0) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n  if (sigaction(SIGTERM, &old_term_act_, 0) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n  if (sigaction(SIGHUP, &old_hup_act_, 0) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n  if (sigaction(SIGCHLD, &old_chld_act_, 0) < 0)\n    Fatal(\"sigaction: %s\", strerror(errno));\n  if (sigprocmask(SIG_SETMASK, &old_mask_, 0) < 0)\n    Fatal(\"sigprocmask: %s\", strerror(errno));\n}\n\nSubprocess *SubprocessSet::Add(const string& command, bool use_console) {\n  Subprocess *subprocess = new Subprocess(use_console);\n  if (!subprocess->Start(this, command)) {\n    delete subprocess;\n    return 0;\n  }\n  running_.push_back(subprocess);\n  return subprocess;\n}\n\n#ifdef USE_PPOLL\nbool SubprocessSet::DoWork() {\n  vector<pollfd> fds;\n  nfds_t nfds = 0;\n\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ++i) {\n    int fd = (*i)->fd_;\n    if (fd < 0)\n      continue;\n    pollfd pfd = { fd, POLLIN | POLLPRI, 0 };\n    fds.push_back(pfd);\n    ++nfds;\n  }\n  if (nfds == 0) {\n    // Add a dummy entry to prevent using an empty pollfd vector.\n    // ppoll() allows to do this by setting fd < 0.\n    pollfd pfd = { -1, 0, 0 };\n    fds.push_back(pfd);\n    ++nfds;\n  }\n\n  interrupted_ = 0;\n  s_sigchld_received = 0;\n  int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);\n  // Note: This can remove console processes from the running set, but that is\n  // not a problem for the pollfd set, as console processes are not part of the\n  // pollfd set (they don't have a fd).\n  CheckConsoleProcessTerminated();\n  if (ret == -1) {\n    if (errno != EINTR) {\n      perror(\"ninja: ppoll\");\n      return false;\n    }\n    return IsInterrupted();\n  }\n\n  // ppoll/pselect prioritizes file descriptor events over a signal delivery.\n  // However, if the user is trying to quit ninja, we should react as fast as\n  // possible.\n  HandlePendingInterruption();\n  if (IsInterrupted())\n    return true;\n\n  // Iterate through both the pollfd set and the running set.\n  // All valid fds in the running set are in the pollfd, in the same order.\n  nfds_t cur_nfd = 0;\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ) {\n    int fd = (*i)->fd_;\n    if (fd < 0) {\n      ++i;\n      continue;\n    }\n    assert(fd == fds[cur_nfd].fd);\n    if (fds[cur_nfd++].revents) {\n      (*i)->OnPipeReady();\n      if ((*i)->Done()) {\n        finished_.push(*i);\n        i = running_.erase(i);\n        continue;\n      }\n    }\n    ++i;\n  }\n\n  return IsInterrupted();\n}\n\n#else  // !defined(USE_PPOLL)\nbool SubprocessSet::DoWork() {\n  fd_set set;\n  int nfds = 0;\n  FD_ZERO(&set);\n\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ++i) {\n    int fd = (*i)->fd_;\n    if (fd >= 0) {\n      FD_SET(fd, &set);\n      if (nfds < fd+1)\n        nfds = fd+1;\n    }\n  }\n\n  interrupted_ = 0;\n  s_sigchld_received = 0;\n  int ret = pselect(nfds, (nfds > 0 ? &set : nullptr), 0, 0, 0, &old_mask_);\n  CheckConsoleProcessTerminated();\n  if (ret == -1) {\n    if (errno != EINTR) {\n      perror(\"ninja: pselect\");\n      return false;\n    }\n    return IsInterrupted();\n  }\n\n  // ppoll/pselect prioritizes file descriptor events over a signal delivery.\n  // However, if the user is trying to quit ninja, we should react as fast as\n  // possible.\n  HandlePendingInterruption();\n  if (IsInterrupted())\n    return true;\n\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ) {\n    int fd = (*i)->fd_;\n    if (fd >= 0 && FD_ISSET(fd, &set)) {\n      (*i)->OnPipeReady();\n      if ((*i)->Done()) {\n        finished_.push(*i);\n        i = running_.erase(i);\n        continue;\n      }\n    }\n    ++i;\n  }\n\n  return IsInterrupted();\n}\n#endif  // !defined(USE_PPOLL)\n\nSubprocess* SubprocessSet::NextFinished() {\n  if (finished_.empty())\n    return NULL;\n  Subprocess* subproc = finished_.front();\n  finished_.pop();\n  return subproc;\n}\n\nvoid SubprocessSet::Clear() {\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ++i)\n    // Since the foreground process is in our process group, it will receive\n    // the interruption signal (i.e. SIGINT or SIGTERM) at the same time as us.\n    if (!(*i)->use_console_)\n      kill(-(*i)->pid_, interrupted_);\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ++i)\n    delete *i;\n  running_.clear();\n}\n"
  },
  {
    "path": "src/subprocess-win32.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"exit_status.h\"\n#include \"subprocess.h\"\n\n#include <assert.h>\n#include <stdio.h>\n\n#include <algorithm>\n\n#include \"util.h\"\n\nusing namespace std;\n\nSubprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(),\n                                           is_reading_(false),\n                                           use_console_(use_console) {\n}\n\nSubprocess::~Subprocess() {\n  if (pipe_) {\n    if (!CloseHandle(pipe_))\n      Win32Fatal(\"CloseHandle\");\n  }\n  // Reap child if forgotten.\n  if (child_)\n    Finish();\n}\n\nHANDLE Subprocess::SetupPipe(HANDLE ioport) {\n  char pipe_name[100];\n  snprintf(pipe_name, sizeof(pipe_name),\n           \"\\\\\\\\.\\\\pipe\\\\ninja_pid%lu_sp%p\", GetCurrentProcessId(), this);\n\n  pipe_ = ::CreateNamedPipeA(pipe_name,\n                             PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,\n                             PIPE_TYPE_BYTE,\n                             PIPE_UNLIMITED_INSTANCES,\n                             0, 0, INFINITE, NULL);\n  if (pipe_ == INVALID_HANDLE_VALUE)\n    Win32Fatal(\"CreateNamedPipe\");\n\n  if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0))\n    Win32Fatal(\"CreateIoCompletionPort\");\n\n  memset(&overlapped_, 0, sizeof(overlapped_));\n  if (!ConnectNamedPipe(pipe_, &overlapped_) &&\n      GetLastError() != ERROR_IO_PENDING) {\n    Win32Fatal(\"ConnectNamedPipe\");\n  }\n\n  // Get the write end of the pipe as a handle inheritable across processes.\n  HANDLE output_write_handle =\n      CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);\n  HANDLE output_write_child;\n  if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,\n                       GetCurrentProcess(), &output_write_child,\n                       0, TRUE, DUPLICATE_SAME_ACCESS)) {\n    Win32Fatal(\"DuplicateHandle\");\n  }\n  CloseHandle(output_write_handle);\n\n  return output_write_child;\n}\n\nbool Subprocess::Start(SubprocessSet* set, const string& command) {\n  HANDLE child_pipe = SetupPipe(set->ioport_);\n\n  SECURITY_ATTRIBUTES security_attributes;\n  memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES));\n  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\n  security_attributes.bInheritHandle = TRUE;\n  // Must be inheritable so subprocesses can dup to children.\n  HANDLE nul =\n      CreateFileA(\"NUL\", GENERIC_READ,\n                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n                  &security_attributes, OPEN_EXISTING, 0, NULL);\n  if (nul == INVALID_HANDLE_VALUE)\n    Fatal(\"couldn't open nul\");\n\n  STARTUPINFOA startup_info;\n  memset(&startup_info, 0, sizeof(startup_info));\n  startup_info.cb = sizeof(STARTUPINFO);\n  if (!use_console_) {\n    startup_info.dwFlags = STARTF_USESTDHANDLES;\n    startup_info.hStdInput = nul;\n    startup_info.hStdOutput = child_pipe;\n    startup_info.hStdError = child_pipe;\n  }\n  // In the console case, child_pipe is still inherited by the child and closed\n  // when the subprocess finishes, which then notifies ninja.\n\n  PROCESS_INFORMATION process_info;\n  memset(&process_info, 0, sizeof(process_info));\n\n  // Ninja handles ctrl-c, except for subprocesses in console pools.\n  DWORD process_flags = use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP;\n\n  // Do not prepend 'cmd /c' on Windows, this breaks command\n  // lines greater than 8,191 chars.\n  if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,\n                      /* inherit handles */ TRUE, process_flags,\n                      NULL, NULL,\n                      &startup_info, &process_info)) {\n    DWORD error = GetLastError();\n    if (error == ERROR_FILE_NOT_FOUND) {\n      // File (program) not found error is treated as a normal build\n      // action failure.\n      if (child_pipe)\n        CloseHandle(child_pipe);\n      CloseHandle(pipe_);\n      CloseHandle(nul);\n      pipe_ = NULL;\n      // child_ is already NULL;\n      buf_ = \"CreateProcess failed: The system cannot find the file \"\n          \"specified.\\n\";\n      return true;\n    } else {\n      fprintf(stderr, \"\\nCreateProcess failed. Command attempted:\\n\\\"%s\\\"\\n\",\n              command.c_str());\n      const char* hint = NULL;\n      // ERROR_INVALID_PARAMETER means the command line was formatted\n      // incorrectly. This can be caused by a command line being too long or\n      // leading whitespace in the command. Give extra context for this case.\n      if (error == ERROR_INVALID_PARAMETER) {\n        if (command.length() > 0 && (command[0] == ' ' || command[0] == '\\t'))\n          hint = \"command contains leading whitespace\";\n        else\n          hint = \"is the command line too long?\";\n      }\n      Win32Fatal(\"CreateProcess\", hint);\n    }\n  }\n\n  // Close pipe channel only used by the child.\n  if (child_pipe)\n    CloseHandle(child_pipe);\n  CloseHandle(nul);\n\n  CloseHandle(process_info.hThread);\n  child_ = process_info.hProcess;\n\n  return true;\n}\n\nvoid Subprocess::OnPipeReady() {\n  DWORD bytes;\n  if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {\n    if (GetLastError() == ERROR_BROKEN_PIPE) {\n      CloseHandle(pipe_);\n      pipe_ = NULL;\n      return;\n    }\n    Win32Fatal(\"GetOverlappedResult\");\n  }\n\n  if (is_reading_ && bytes)\n    buf_.append(overlapped_buf_, bytes);\n\n  memset(&overlapped_, 0, sizeof(overlapped_));\n  is_reading_ = true;\n  if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),\n                  &bytes, &overlapped_)) {\n    if (GetLastError() == ERROR_BROKEN_PIPE) {\n      CloseHandle(pipe_);\n      pipe_ = NULL;\n      return;\n    }\n    if (GetLastError() != ERROR_IO_PENDING)\n      Win32Fatal(\"ReadFile\");\n  }\n\n  // Even if we read any bytes in the readfile call, we'll enter this\n  // function again later and get them at that point.\n}\n\nExitStatus Subprocess::Finish() {\n  if (!child_)\n    return ExitFailure;\n\n  // TODO: add error handling for all of these.\n  WaitForSingleObject(child_, INFINITE);\n\n  DWORD exit_code = 0;\n  GetExitCodeProcess(child_, &exit_code);\n\n  CloseHandle(child_);\n  child_ = NULL;\n\n  return exit_code == CONTROL_C_EXIT ? ExitInterrupted :\n                                       static_cast<ExitStatus>(exit_code);\n}\n\nbool Subprocess::Done() const {\n  return pipe_ == NULL;\n}\n\nconst string& Subprocess::GetOutput() const {\n  return buf_;\n}\n\nHANDLE SubprocessSet::ioport_;\n\nSubprocessSet::SubprocessSet() {\n  ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);\n  if (!ioport_)\n    Win32Fatal(\"CreateIoCompletionPort\");\n  if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))\n    Win32Fatal(\"SetConsoleCtrlHandler\");\n}\n\nSubprocessSet::~SubprocessSet() {\n  Clear();\n\n  SetConsoleCtrlHandler(NotifyInterrupted, FALSE);\n  CloseHandle(ioport_);\n}\n\nBOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {\n  if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {\n    if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))\n      Win32Fatal(\"PostQueuedCompletionStatus\");\n    return TRUE;\n  }\n\n  return FALSE;\n}\n\nSubprocess *SubprocessSet::Add(const string& command, bool use_console) {\n  Subprocess *subprocess = new Subprocess(use_console);\n  if (!subprocess->Start(this, command)) {\n    delete subprocess;\n    return 0;\n  }\n  if (subprocess->child_)\n    running_.push_back(subprocess);\n  else\n    finished_.push(subprocess);\n  return subprocess;\n}\n\nbool SubprocessSet::DoWork() {\n  DWORD bytes_read;\n  Subprocess* subproc;\n  OVERLAPPED* overlapped;\n\n  if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,\n                                 &overlapped, INFINITE)) {\n    if (GetLastError() != ERROR_BROKEN_PIPE)\n      Win32Fatal(\"GetQueuedCompletionStatus\");\n  }\n\n  if (!subproc) // A NULL subproc indicates that we were interrupted and is\n                // delivered by NotifyInterrupted above.\n    return true;\n\n  subproc->OnPipeReady();\n\n  if (subproc->Done()) {\n    vector<Subprocess*>::iterator end =\n        remove(running_.begin(), running_.end(), subproc);\n    if (running_.end() != end) {\n      finished_.push(subproc);\n      running_.resize(end - running_.begin());\n    }\n  }\n\n  return false;\n}\n\nSubprocess* SubprocessSet::NextFinished() {\n  if (finished_.empty())\n    return NULL;\n  Subprocess* subproc = finished_.front();\n  finished_.pop();\n  return subproc;\n}\n\nvoid SubprocessSet::Clear() {\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ++i) {\n    // Since the foreground process is in our process group, it will receive a\n    // CTRL_C_EVENT or CTRL_BREAK_EVENT at the same time as us.\n    if ((*i)->child_ && !(*i)->use_console_) {\n      if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,\n                                    GetProcessId((*i)->child_))) {\n        Win32Fatal(\"GenerateConsoleCtrlEvent\");\n      }\n    }\n  }\n  for (vector<Subprocess*>::iterator i = running_.begin();\n       i != running_.end(); ++i)\n    delete *i;\n  running_.clear();\n}\n"
  },
  {
    "path": "src/subprocess.h",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_SUBPROCESS_H_\n#define NINJA_SUBPROCESS_H_\n\n#include <string>\n#include <vector>\n#include <queue>\n\n#ifdef _WIN32\n#include <windows.h>\n#else\n#include <signal.h>\n#endif\n\n// ppoll() exists on FreeBSD, but only on newer versions.\n#ifdef __FreeBSD__\n#  include <sys/param.h>\n#  if defined USE_PPOLL && __FreeBSD_version < 1002000\n#    undef USE_PPOLL\n#  endif\n#endif\n\n#include \"exit_status.h\"\n\n/// Subprocess wraps a single async subprocess.  It is entirely\n/// passive: it expects the caller to notify it when its fds are ready\n/// for reading, as well as call Finish() to reap the child once done()\n/// is true.\nstruct Subprocess {\n  ~Subprocess();\n\n  /// Returns ExitSuccess on successful process exit, ExitInterrupted if\n  /// the process was interrupted, ExitFailure if it otherwise failed.\n  ExitStatus Finish();\n\n  bool Done() const;\n\n  const std::string& GetOutput() const;\n\n private:\n  Subprocess(bool use_console);\n  bool Start(struct SubprocessSet* set, const std::string& command);\n  void OnPipeReady();\n\n  std::string buf_;\n\n#ifdef _WIN32\n  /// Set up pipe_ as the parent-side pipe of the subprocess; return the\n  /// other end of the pipe, usable in the child process.\n  HANDLE SetupPipe(HANDLE ioport);\n\n  HANDLE child_;\n  HANDLE pipe_;\n  OVERLAPPED overlapped_;\n  char overlapped_buf_[4 << 10];\n  bool is_reading_;\n#else\n  /// The file descriptor that will be used in ppoll/pselect() for this process,\n  /// if any. Otherwise -1.\n  /// In non-console mode, this is the read-side of a pipe that was created\n  /// specifically for this subprocess. The write-side of the pipe is given to\n  /// the subprocess as combined stdout and stderr.\n  /// In console mode no pipe is created: fd_ is -1, and process termination is\n  /// detected using the SIGCHLD signal and waitpid(WNOHANG).\n  int fd_;\n  /// PID of the subprocess. Set to -1 when the subprocess is reaped.\n  pid_t pid_;\n  /// In POSIX platforms it is necessary to use waitpid(WNOHANG) to know whether\n  /// a certain subprocess has finished. This is done for terminal subprocesses.\n  /// However, this also causes the subprocess to be reaped before Finish() is\n  /// called, so we need to store the ExitStatus so that a later Finish()\n  /// invocation can return it.\n  ExitStatus exit_status_;\n\n  /// Call waitpid() on the subprocess with the provided options and update the\n  /// pid_ and exit_status_ fields.\n  /// Return a boolean indicating whether the subprocess has indeed terminated.\n  bool TryFinish(int waitpid_options);\n#endif\n  bool use_console_;\n\n  friend struct SubprocessSet;\n};\n\n/// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.\n/// DoWork() waits for any state change in subprocesses; finished_\n/// is a queue of subprocesses as they finish.\nstruct SubprocessSet {\n  SubprocessSet();\n  ~SubprocessSet();\n\n  Subprocess* Add(const std::string& command, bool use_console = false);\n  bool DoWork();\n  Subprocess* NextFinished();\n  void Clear();\n\n  std::vector<Subprocess*> running_;\n  std::queue<Subprocess*> finished_;\n\n#ifdef _WIN32\n  static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType);\n  static HANDLE ioport_;\n#else\n  static void SetInterruptedFlag(int signum);\n  static void SigChldHandler(int signo, siginfo_t* info, void* context);\n\n  /// Store the signal number that causes the interruption.\n  /// 0 if not interruption.\n  static volatile sig_atomic_t interrupted_;\n  /// Whether ninja should quit. Set on SIGINT, SIGTERM or SIGHUP reception.\n  static bool IsInterrupted() { return interrupted_ != 0; }\n  static void HandlePendingInterruption();\n\n  /// Initialized to 0 before ppoll/pselect().\n  /// Filled to 1 by SIGCHLD handler when a child process terminates.\n  static volatile sig_atomic_t s_sigchld_received;\n  void CheckConsoleProcessTerminated();\n\n  struct sigaction old_int_act_;\n  struct sigaction old_term_act_;\n  struct sigaction old_hup_act_;\n  struct sigaction old_chld_act_;\n  sigset_t old_mask_;\n#endif\n};\n\n#endif // NINJA_SUBPROCESS_H_\n"
  },
  {
    "path": "src/subprocess_test.cc",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"subprocess.h\"\n\n#include \"exit_status.h\"\n#include \"test.h\"\n\n#ifndef _WIN32\n// SetWithLots need setrlimit.\n#include <stdio.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <unistd.h>\n#endif\n\nusing namespace std;\n\nnamespace {\n\n#ifdef _WIN32\nconst char* kSimpleCommand = \"cmd /c dir \\\\\";\n#else\nconst char* kSimpleCommand = \"ls /\";\n#endif\n\nstruct SubprocessTest : public testing::Test {\n  SubprocessSet subprocs_;\n};\n\n}  // anonymous namespace\n\n// Run a command that fails and emits to stderr.\nTEST_F(SubprocessTest, BadCommandStderr) {\n  Subprocess* subproc = subprocs_.Add(\"cmd /c ninja_no_such_command\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    // Pretend we discovered that stderr was ready for writing.\n    subprocs_.DoWork();\n  }\n\n  ExitStatus exit = subproc->Finish();\n  EXPECT_NE(ExitSuccess, exit);\n  EXPECT_NE(\"\", subproc->GetOutput());\n}\n\n// Run a command that does not exist\nTEST_F(SubprocessTest, NoSuchCommand) {\n  Subprocess* subproc = subprocs_.Add(\"ninja_no_such_command\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    // Pretend we discovered that stderr was ready for writing.\n    subprocs_.DoWork();\n  }\n\n  ExitStatus exit = subproc->Finish();\n  EXPECT_NE(ExitSuccess, exit);\n  EXPECT_NE(\"\", subproc->GetOutput());\n#ifdef _WIN32\n  ASSERT_EQ(\"CreateProcess failed: The system cannot find the file \"\n            \"specified.\\n\", subproc->GetOutput());\n#endif\n}\n\n#ifndef _WIN32\n\nTEST_F(SubprocessTest, InterruptChild) {\n  Subprocess* subproc = subprocs_.Add(\"kill -INT $$\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    subprocs_.DoWork();\n  }\n\n  EXPECT_EQ(ExitInterrupted, subproc->Finish());\n}\n\nTEST_F(SubprocessTest, InterruptParent) {\n  Subprocess* subproc = subprocs_.Add(\"kill -INT $PPID ; sleep 1\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    bool interrupted = subprocs_.DoWork();\n    if (interrupted)\n      return;\n  }\n\n  ASSERT_FALSE(\"We should have been interrupted\");\n}\n\nTEST_F(SubprocessTest, InterruptChildWithSigTerm) {\n  Subprocess* subproc = subprocs_.Add(\"kill -TERM $$\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    subprocs_.DoWork();\n  }\n\n  EXPECT_EQ(ExitInterrupted, subproc->Finish());\n}\n\nTEST_F(SubprocessTest, InterruptParentWithSigTerm) {\n  Subprocess* subproc = subprocs_.Add(\"kill -TERM $PPID ; sleep 1\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    bool interrupted = subprocs_.DoWork();\n    if (interrupted)\n      return;\n  }\n\n  ASSERT_FALSE(\"We should have been interrupted\");\n}\n\nTEST_F(SubprocessTest, InterruptChildWithSigHup) {\n  Subprocess* subproc = subprocs_.Add(\"kill -HUP $$\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    subprocs_.DoWork();\n  }\n\n  EXPECT_EQ(ExitInterrupted, subproc->Finish());\n}\n\nTEST_F(SubprocessTest, InterruptParentWithSigHup) {\n  Subprocess* subproc = subprocs_.Add(\"kill -HUP $PPID ; sleep 1\");\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    bool interrupted = subprocs_.DoWork();\n    if (interrupted)\n      return;\n  }\n\n  ASSERT_FALSE(\"We should have been interrupted\");\n}\n\nTEST_F(SubprocessTest, Console) {\n  // Skip test if we don't have the console ourselves.\n  if (isatty(0) && isatty(1) && isatty(2)) {\n    Subprocess* subproc =\n        subprocs_.Add(\"test -t 0 -a -t 1 -a -t 2\", /*use_console=*/true);\n    ASSERT_NE((Subprocess*)0, subproc);\n\n    while (!subproc->Done()) {\n      subprocs_.DoWork();\n    }\n\n    EXPECT_EQ(ExitSuccess, subproc->Finish());\n  }\n}\n\n#endif\n\nTEST_F(SubprocessTest, SetWithSingle) {\n  Subprocess* subproc = subprocs_.Add(kSimpleCommand);\n  ASSERT_NE((Subprocess *) 0, subproc);\n\n  while (!subproc->Done()) {\n    subprocs_.DoWork();\n  }\n  ASSERT_EQ(ExitSuccess, subproc->Finish());\n  ASSERT_NE(\"\", subproc->GetOutput());\n\n  ASSERT_EQ(1u, subprocs_.finished_.size());\n}\n\nTEST_F(SubprocessTest, SetWithMulti) {\n  Subprocess* processes[3];\n  const char* kCommands[3] = {\n    kSimpleCommand,\n#ifdef _WIN32\n    \"cmd /c echo hi\",\n    \"cmd /c time /t\",\n#else\n    \"id -u\",\n    \"pwd\",\n#endif\n  };\n\n  for (int i = 0; i < 3; ++i) {\n    processes[i] = subprocs_.Add(kCommands[i]);\n    ASSERT_NE((Subprocess *) 0, processes[i]);\n  }\n\n  ASSERT_EQ(3u, subprocs_.running_.size());\n  for (int i = 0; i < 3; ++i) {\n    ASSERT_FALSE(processes[i]->Done());\n    ASSERT_EQ(\"\", processes[i]->GetOutput());\n  }\n\n  while (!processes[0]->Done() || !processes[1]->Done() ||\n         !processes[2]->Done()) {\n    ASSERT_GT(subprocs_.running_.size(), 0u);\n    subprocs_.DoWork();\n  }\n\n  ASSERT_EQ(0u, subprocs_.running_.size());\n  ASSERT_EQ(3u, subprocs_.finished_.size());\n\n  for (int i = 0; i < 3; ++i) {\n    ASSERT_EQ(ExitSuccess, processes[i]->Finish());\n    ASSERT_NE(\"\", processes[i]->GetOutput());\n    delete processes[i];\n  }\n}\n\n#if defined(USE_PPOLL)\nTEST_F(SubprocessTest, SetWithLots) {\n  // Arbitrary big number; needs to be over 1024 to confirm we're no longer\n  // hostage to pselect.\n  const unsigned kNumProcs = 1025;\n\n  // Make sure [ulimit -n] isn't going to stop us from working.\n  rlimit rlim;\n  ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim));\n  if (rlim.rlim_cur < kNumProcs) {\n    printf(\"Raise [ulimit -n] above %u (currently %lu) to make this test go\\n\",\n           kNumProcs, static_cast<unsigned long>(rlim.rlim_cur));\n    return;\n  }\n\n  vector<Subprocess*> procs;\n  for (size_t i = 0; i < kNumProcs; ++i) {\n    Subprocess* subproc = subprocs_.Add(\"/bin/echo\");\n    ASSERT_NE((Subprocess *) 0, subproc);\n    procs.push_back(subproc);\n  }\n  while (!subprocs_.running_.empty())\n    subprocs_.DoWork();\n  for (size_t i = 0; i < procs.size(); ++i) {\n    ASSERT_EQ(ExitSuccess, procs[i]->Finish());\n    ASSERT_NE(\"\", procs[i]->GetOutput());\n  }\n  ASSERT_EQ(kNumProcs, subprocs_.finished_.size());\n}\n#endif  // !__APPLE__ && !_WIN32\n\n// TODO: this test could work on Windows, just not sure how to simply\n// read stdin.\n#ifndef _WIN32\n// Verify that a command that attempts to read stdin correctly thinks\n// that stdin is closed.\nTEST_F(SubprocessTest, ReadStdin) {\n  Subprocess* subproc = subprocs_.Add(\"cat -\");\n  while (!subproc->Done()) {\n    subprocs_.DoWork();\n  }\n  ASSERT_EQ(ExitSuccess, subproc->Finish());\n  ASSERT_EQ(1u, subprocs_.finished_.size());\n}\n#endif  // _WIN32\n"
  },
  {
    "path": "src/test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifdef _WIN32\n#include <direct.h>  // Has to be before util.h is included.\n#endif\n\n#include \"test.h\"\n\n#include <algorithm>\n\n#include <errno.h>\n#include <stdlib.h>\n#ifdef _WIN32\n#include <windows.h>\n#include <io.h>\n#else\n#include <unistd.h>\n#endif\n\n#include \"build_log.h\"\n#include \"graph.h\"\n#include \"manifest_parser.h\"\n#include \"util.h\"\n\n#ifdef _AIX\nextern \"C\" {\n        // GCC \"helpfully\" strips the definition of mkdtemp out on AIX.\n        // The function is still present, so if we define it ourselves\n        // it will work perfectly fine.\n        extern char* mkdtemp(char* name_template);\n}\n#endif\n\nusing namespace std;\n\nnamespace {\n\n#ifdef _WIN32\n/// Windows has no mkdtemp.  Implement it in terms of _mktemp_s.\nchar* mkdtemp(char* name_template) {\n  int err = _mktemp_s(name_template, strlen(name_template) + 1);\n  if (err < 0) {\n    perror(\"_mktemp_s\");\n    return NULL;\n  }\n\n  err = _mkdir(name_template);\n  if (err < 0) {\n    perror(\"mkdir\");\n    return NULL;\n  }\n\n  return name_template;\n}\n#endif  // _WIN32\n\nstring GetSystemTempDir() {\n#ifdef _WIN32\n  char buf[1024];\n  if (!GetTempPath(sizeof(buf), buf))\n    return \"\";\n  return buf;\n#else\n  const char* tempdir = getenv(\"TMPDIR\");\n  if (tempdir)\n    return tempdir;\n  return \"/tmp\";\n#endif\n}\n\n}  // anonymous namespace\n\nStateTestWithBuiltinRules::StateTestWithBuiltinRules() {\n  AddCatRule(&state_);\n}\n\nvoid StateTestWithBuiltinRules::AddCatRule(State* state) {\n  AssertParse(state,\n\"rule cat\\n\"\n\"  command = cat $in > $out\\n\");\n}\n\nNode* StateTestWithBuiltinRules::GetNode(const string& path) {\n  EXPECT_FALSE(strpbrk(path.c_str(), \"/\\\\\"));\n  return state_.GetNode(path, 0);\n}\n\nvoid AssertParse(State* state, const char* input,\n                 ManifestParserOptions opts) {\n  ManifestParser parser(state, NULL, opts);\n  string err;\n  EXPECT_TRUE(parser.ParseTest(input, &err));\n  ASSERT_EQ(\"\", err);\n  VerifyGraph(*state);\n}\n\nvoid AssertHash(const char* expected, uint64_t actual) {\n  ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);\n}\n\nvoid VerifyGraph(const State& state) {\n  for (vector<Edge*>::const_iterator e = state.edges_.begin();\n       e != state.edges_.end(); ++e) {\n    // All edges need at least one output.\n    EXPECT_FALSE((*e)->outputs_.empty());\n    // Check that the edge's inputs have the edge as out-edge.\n    for (vector<Node*>::const_iterator in_node = (*e)->inputs_.begin();\n         in_node != (*e)->inputs_.end(); ++in_node) {\n      const vector<Edge*>& out_edges = (*in_node)->out_edges();\n      EXPECT_NE(find(out_edges.begin(), out_edges.end(), *e),\n                out_edges.end());\n    }\n    // Check that the edge's outputs have the edge as in-edge.\n    for (vector<Node*>::const_iterator out_node = (*e)->outputs_.begin();\n         out_node != (*e)->outputs_.end(); ++out_node) {\n      EXPECT_EQ((*out_node)->in_edge(), *e);\n    }\n  }\n\n  // The union of all in- and out-edges of each nodes should be exactly edges_.\n  set<const Edge*> node_edge_set;\n  for (State::Paths::const_iterator p = state.paths_.begin();\n       p != state.paths_.end(); ++p) {\n    const Node* n = p->second;\n    if (n->in_edge())\n      node_edge_set.insert(n->in_edge());\n    node_edge_set.insert(n->out_edges().begin(), n->out_edges().end());\n  }\n  set<const Edge*> edge_set(state.edges_.begin(), state.edges_.end());\n  EXPECT_EQ(node_edge_set, edge_set);\n}\n\nvoid VirtualFileSystem::Create(const string& path,\n                               const string& contents) {\n  files_[path].mtime = now_;\n  files_[path].contents = contents;\n  files_created_.insert(path);\n}\n\nTimeStamp VirtualFileSystem::Stat(const string& path, string* err) const {\n  FileMap::const_iterator i = files_.find(path);\n  if (i != files_.end()) {\n    if (!i->second.stat_error.empty()) {\n      *err = i->second.stat_error;\n      return -1;\n    }\n    assert(i->second.mtime > 0);\n    return i->second.mtime;\n  }\n  return 0;\n}\n\nbool VirtualFileSystem::WriteFile(const string& path, const string& contents,\n                                  bool /*crlf_on_windows*/) {\n  Create(path, contents);\n  return true;\n}\n\nbool VirtualFileSystem::MakeDir(const string& path) {\n  directories_made_.push_back(path);\n  return true;  // success\n}\n\nFileReader::Status VirtualFileSystem::ReadFile(const string& path,\n                                               string* contents,\n                                               string* err) {\n  files_read_.push_back(path);\n  FileMap::iterator i = files_.find(path);\n  if (i != files_.end()) {\n    *contents = i->second.contents;\n    return Okay;\n  }\n  *err = strerror(ENOENT);\n  return NotFound;\n}\n\nint VirtualFileSystem::RemoveFile(const string& path) {\n  if (find(directories_made_.begin(), directories_made_.end(), path)\n      != directories_made_.end())\n    return -1;\n  FileMap::iterator i = files_.find(path);\n  if (i != files_.end()) {\n    files_.erase(i);\n    files_removed_.insert(path);\n    return 0;\n  } else {\n    return 1;\n  }\n}\n\nvoid ScopedTempDir::CreateAndEnter(const string& name) {\n  // First change into the system temp dir and save it for cleanup.\n  start_dir_ = GetSystemTempDir();\n  if (start_dir_.empty())\n    Fatal(\"couldn't get system temp dir\");\n  if (chdir(start_dir_.c_str()) < 0)\n    Fatal(\"chdir: %s\", strerror(errno));\n\n  // Create a temporary subdirectory of that.\n  char name_template[1024];\n  strcpy(name_template, name.c_str());\n  strcat(name_template, \"-XXXXXX\");\n  char* tempname = mkdtemp(name_template);\n  if (!tempname)\n    Fatal(\"mkdtemp: %s\", strerror(errno));\n  temp_dir_name_ = tempname;\n\n  // chdir into the new temporary directory.\n  if (chdir(temp_dir_name_.c_str()) < 0)\n    Fatal(\"chdir: %s\", strerror(errno));\n}\n\nvoid ScopedTempDir::Cleanup() {\n  if (temp_dir_name_.empty())\n    return;  // Something went wrong earlier.\n\n  // Move out of the directory we're about to clobber.\n  if (chdir(start_dir_.c_str()) < 0)\n    Fatal(\"chdir: %s\", strerror(errno));\n\n#ifdef _WIN32\n  string command = \"rmdir /s /q \" + temp_dir_name_;\n#else\n  string command = \"rm -rf \" + temp_dir_name_;\n#endif\n  if (system(command.c_str()) < 0)\n    Fatal(\"system: %s\", strerror(errno));\n\n  temp_dir_name_.clear();\n}\n\nScopedFilePath::ScopedFilePath(ScopedFilePath&& other) noexcept\n    : path_(std::move(other.path_)), released_(other.released_) {\n  other.released_ = true;\n}\n\n/// It would be nice to use '= default' here instead but some old compilers\n/// such as GCC from Ubuntu 16.06 will not compile it with \"noexcept\", so just\n/// write it manually.\nScopedFilePath& ScopedFilePath::operator=(ScopedFilePath&& other) noexcept {\n  if (this != &other) {\n    this->~ScopedFilePath();\n    new (this) ScopedFilePath(std::move(other));\n  }\n  return *this;\n}\n\nScopedFilePath::~ScopedFilePath() {\n  if (!released_) {\n    platformAwareUnlink(path_.c_str());\n  }\n}\n\nvoid ScopedFilePath::Release() {\n  released_ = true;\n}\n"
  },
  {
    "path": "src/test.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_TEST_H_\n#define NINJA_TEST_H_\n\n#include <gtest/gtest.h>\n\n#include \"disk_interface.h\"\n#include \"manifest_parser.h\"\n#include \"state.h\"\n\n// Support utilities for tests.\n\nstruct Node;\n\n/// A base test fixture that includes a State object with a\n/// builtin \"cat\" rule.\nstruct StateTestWithBuiltinRules : public testing::Test {\n  StateTestWithBuiltinRules();\n\n  /// Add a \"cat\" rule to \\a state.  Used by some tests; it's\n  /// otherwise done by the ctor to state_.\n  void AddCatRule(State* state);\n\n  /// Short way to get a Node by its path from state_.\n  Node* GetNode(const std::string& path);\n\n  State state_;\n};\n\nvoid AssertParse(State* state, const char* input,\n                 ManifestParserOptions = ManifestParserOptions());\nvoid AssertHash(const char* expected, uint64_t actual);\nvoid VerifyGraph(const State& state);\n\n/// An implementation of DiskInterface that uses an in-memory representation\n/// of disk state.  It also logs file accesses and directory creations\n/// so it can be used by tests to verify disk access patterns.\nstruct VirtualFileSystem : public DiskInterface {\n  VirtualFileSystem() : now_(1) {}\n\n  /// \"Create\" a file with contents.\n  void Create(const std::string& path, const std::string& contents);\n\n  /// Tick \"time\" forwards; subsequent file operations will be newer than\n  /// previous ones.\n  int Tick() {\n    return ++now_;\n  }\n\n  // DiskInterface\n  TimeStamp Stat(const std::string& path, std::string* err) const override;\n  bool WriteFile(const std::string& path, const std::string& contents,\n                 bool /*crlf_on_windows*/) override;\n  bool MakeDir(const std::string& path) override;\n  Status ReadFile(const std::string& path, std::string* contents,\n                  std::string* err) override;\n  int RemoveFile(const std::string& path) override;\n\n  /// An entry for a single in-memory file.\n  struct Entry {\n    int mtime;\n    std::string stat_error;  // If mtime is -1.\n    std::string contents;\n  };\n\n  std::vector<std::string> directories_made_;\n  std::vector<std::string> files_read_;\n  typedef std::map<std::string, Entry> FileMap;\n  FileMap files_;\n  std::set<std::string> files_removed_;\n  std::set<std::string> files_created_;\n\n  /// A simple fake timestamp for file operations.\n  int now_;\n};\n\nstruct ScopedTempDir {\n  /// Create a temporary directory and chdir into it.\n  void CreateAndEnter(const std::string& name);\n\n  /// Clean up the temporary directory.\n  void Cleanup();\n\n  /// The temp directory containing our dir.\n  std::string start_dir_;\n  /// The subdirectory name for our dir, or empty if it hasn't been set up.\n  std::string temp_dir_name_;\n};\n\n/// A class that records a file path and ensures that it is removed\n/// on destruction. This ensures that tests do not keep stale files in the\n/// current directory where they run, even in case of assertion failure.\nstruct ScopedFilePath {\n  /// Constructor just records the file path.\n  ScopedFilePath(const std::string& path) : path_(path) {}\n  ScopedFilePath(const char* path) : path_(path) {}\n\n  /// Allow move operations.\n  ScopedFilePath(ScopedFilePath&&) noexcept;\n  ScopedFilePath& operator=(ScopedFilePath&&) noexcept;\n\n  /// Destructor destroys the file, unless Release() was called.\n  ~ScopedFilePath();\n\n  /// Release the file, the destructor will not remove the file.\n  void Release();\n\n  const char* c_str() const { return path_.c_str(); }\n  const std::string& path() const { return path_; }\n  bool released() const { return released_; }\n\n private:\n  std::string path_;\n  bool released_ = false;\n};\n\n#endif // NINJA_TEST_H_\n"
  },
  {
    "path": "src/third_party/emhash/README.ninja",
    "content": "Description: emhash8::HashMap for C++14/17\nVersion: 1.6.5 (commit bdebddbdce1b473bbc189178fd523ef4a876ea01)\nURL: https://github.com/ktprime/emhash\nCopyright: Copyright (c) 2021-2024 Huang Yuanbing & bailuzhou AT 163.com\nSPDX-License-Identifier: MIT\nLocal changes:\n - Added includes for _mm_prefetch on MinGW.\n - Fixed some spelling errors to appease the linter.\n"
  },
  {
    "path": "src/third_party/emhash/hash_table8.hpp",
    "content": "// emhash8::HashMap for C++14/17\n// version 1.6.5\n// https://github.com/ktprime/emhash/blob/master/hash_table8.hpp\n//\n// Licensed under the MIT License <http://opensource.org/licenses/MIT>.\n// SPDX-License-Identifier: MIT\n// Copyright (c) 2021-2024 Huang Yuanbing & bailuzhou AT 163.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE\n\n#pragma once\n\n#include <cstring>\n#include <string>\n#include <cstdlib>\n#include <type_traits>\n#include <cassert>\n#include <utility>\n#include <cstdint>\n#include <functional>\n#include <iterator>\n#include <algorithm>\n#include <memory>\n\n#undef  EMH_NEW\n#undef  EMH_EMPTY\n\n// likely/unlikely\n#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)\n#    define EMH_LIKELY(condition)   __builtin_expect(condition, 1)\n#    define EMH_UNLIKELY(condition) __builtin_expect(condition, 0)\n#else\n#    define EMH_LIKELY(condition)   condition\n#    define EMH_UNLIKELY(condition) condition\n#endif\n\n#define EMH_EMPTY(n) (0 > (int)(_index[n].next))\n#define EMH_EQHASH(n, key_hash) (((size_type)(key_hash) & ~_mask) == (_index[n].slot & ~_mask))\n//#define EMH_EQHASH(n, key_hash) ((size_type)(key_hash - _index[n].slot) & ~_mask) == 0\n#define EMH_NEW(key, val, bucket, key_hash) \\\n    new(_pairs + _num_filled) value_type(key, val); \\\n    _etail = bucket; \\\n    _index[bucket] = {bucket, _num_filled++ | ((size_type)(key_hash) & ~_mask)}\n\n#if _WIN32 && defined(_M_IX86)\n#include <xmmintrin.h>\n#endif\n\nnamespace emhash8 {\n\nstruct DefaultPolicy {\n    static constexpr float load_factor = 0.80f;\n    static constexpr float min_load_factor = 0.20f;\n    static constexpr size_t cacheline_size = 64U;\n};\n\ntemplate<typename KeyT, typename ValueT,\n         typename HashT = std::hash<KeyT>,\n         typename EqT = std::equal_to<KeyT>,\n         typename Allocator = std::allocator<std::pair<KeyT, ValueT>>, //never used\n         typename Policy = DefaultPolicy> //never used\nclass HashMap\n{\n#ifndef EMH_DEFAULT_LOAD_FACTOR\n    constexpr static float EMH_DEFAULT_LOAD_FACTOR = 0.80f;\n#endif\n    constexpr static float EMH_MIN_LOAD_FACTOR     = 0.25f; //< 0.5\n    constexpr static uint32_t EMH_CACHE_LINE_SIZE  = 64; //debug only\n\npublic:\n    using htype = HashMap<KeyT, ValueT, HashT, EqT>;\n    using value_type = std::pair<KeyT, ValueT>;\n    using key_type = KeyT;\n    using mapped_type = ValueT;\n    //using dPolicy = Policy;\n\n#ifdef EMH_SMALL_TYPE\n    using size_type = uint16_t;\n#elif EMH_SIZE_TYPE == 0\n    using size_type = uint32_t;\n#else\n    using size_type = size_t;\n#endif\n\n    using hasher = HashT;\n    using key_equal = EqT;\n\n    constexpr static size_type INACTIVE = 0-1u;\n    //constexpr uint32_t END      = 0-0x1u;\n    constexpr static size_type EAD      = 2;\n\n    struct Index\n    {\n        size_type next;\n        size_type slot;\n    };\n\n    class const_iterator;\n    class iterator\n    {\n    public:\n        using iterator_category = std::bidirectional_iterator_tag;\n        using difference_type = std::ptrdiff_t;\n        using value_type      = typename htype::value_type;\n        using pointer         = value_type*;\n        using const_pointer   = const value_type* ;\n        using reference       = value_type&;\n        using const_reference = const value_type&;\n\n        iterator() : kv_(nullptr) {}\n        iterator(const_iterator& cit) {\n            kv_ = cit.kv_;\n        }\n\n        iterator(const htype* hash_map, size_type bucket) {\n            kv_ = hash_map->_pairs + (int)bucket;\n        }\n\n        iterator& operator++()\n        {\n            kv_ ++;\n            return *this;\n        }\n\n        iterator operator++(int)\n        {\n            auto cur = *this; kv_ ++;\n            return cur;\n        }\n\n        iterator& operator--()\n        {\n            kv_ --;\n            return *this;\n        }\n\n        iterator operator--(int)\n        {\n            auto cur = *this; kv_ --;\n            return cur;\n        }\n\n        reference operator*() const { return *kv_; }\n        pointer operator->() const { return kv_; }\n\n        bool operator == (const iterator& rhs) const { return kv_ == rhs.kv_; }\n        bool operator != (const iterator& rhs) const { return kv_ != rhs.kv_; }\n        bool operator == (const const_iterator& rhs) const { return kv_ == rhs.kv_; }\n        bool operator != (const const_iterator& rhs) const { return kv_ != rhs.kv_; }\n\n    public:\n        value_type* kv_;\n    };\n\n    class const_iterator\n    {\n    public:\n        using iterator_category = std::bidirectional_iterator_tag;\n        using value_type        = typename htype::value_type;\n        using difference_type   = std::ptrdiff_t;\n        using pointer           = value_type*;\n        using const_pointer     = const value_type*;\n        using reference         = value_type&;\n        using const_reference   = const value_type&;\n\n        const_iterator(const iterator& it) {\n            kv_ = it.kv_;\n        }\n\n        const_iterator (const htype* hash_map, size_type bucket) {\n            kv_ = hash_map->_pairs + (int)bucket;\n        }\n\n        const_iterator& operator++()\n        {\n            kv_ ++;\n            return *this;\n        }\n\n        const_iterator operator++(int)\n        {\n            auto cur = *this; kv_ ++;\n            return cur;\n        }\n\n        const_iterator& operator--()\n        {\n            kv_ --;\n            return *this;\n        }\n\n        const_iterator operator--(int)\n        {\n            auto cur = *this; kv_ --;\n            return cur;\n        }\n\n        const_reference operator*() const { return *kv_; }\n        const_pointer operator->() const { return kv_; }\n\n        bool operator == (const iterator& rhs) const { return kv_ == rhs.kv_; }\n        bool operator != (const iterator& rhs) const { return kv_ != rhs.kv_; }\n        bool operator == (const const_iterator& rhs) const { return kv_ == rhs.kv_; }\n        bool operator != (const const_iterator& rhs) const { return kv_ != rhs.kv_; }\n    public:\n        const value_type* kv_;\n    };\n\n    void init(size_type bucket, float mlf = EMH_DEFAULT_LOAD_FACTOR)\n    {\n        _pairs = nullptr;\n        _index = nullptr;\n        _mask  = _num_buckets = 0;\n        _num_filled = 0;\n        _mlf = (uint32_t)((1 << 27) / EMH_DEFAULT_LOAD_FACTOR);\n        max_load_factor(mlf);\n        rehash(bucket);\n    }\n\n    HashMap(size_type bucket = 2, float mlf = EMH_DEFAULT_LOAD_FACTOR)\n    {\n        init(bucket, mlf);\n    }\n\n    HashMap(const HashMap& rhs)\n    {\n        if (rhs.load_factor() > EMH_MIN_LOAD_FACTOR) {\n            _pairs = alloc_bucket((size_type)(rhs._num_buckets * rhs.max_load_factor()) + 4);\n            _index = alloc_index(rhs._num_buckets);\n            clone(rhs);\n        } else {\n            init(rhs._num_filled + 2, rhs.max_load_factor());\n            for (auto it = rhs.begin(); it != rhs.end(); ++it)\n                insert_unique(it->first, it->second);\n        }\n    }\n\n    HashMap(HashMap&& rhs) noexcept\n    {\n        init(0);\n        *this = std::move(rhs);\n    }\n\n    HashMap(std::initializer_list<value_type> ilist)\n    {\n        init((size_type)ilist.size());\n        for (auto it = ilist.begin(); it != ilist.end(); ++it)\n            do_insert(*it);\n    }\n\n    template<class InputIt>\n    HashMap(InputIt first, InputIt last, size_type bucket_count=4)\n    {\n        init(std::distance(first, last) + bucket_count);\n        for (; first != last; ++first)\n            emplace(*first);\n    }\n\n    HashMap& operator=(const HashMap& rhs)\n    {\n        if (this == &rhs)\n            return *this;\n\n        if (rhs.load_factor() < EMH_MIN_LOAD_FACTOR) {\n            clear(); free(_pairs); _pairs = nullptr;\n            rehash(rhs._num_filled + 2);\n            for (auto it = rhs.begin(); it != rhs.end(); ++it)\n                insert_unique(it->first, it->second);\n            return *this;\n        }\n\n        clearkv();\n\n        if (_num_buckets != rhs._num_buckets) {\n            free(_pairs); free(_index);\n            _index = alloc_index(rhs._num_buckets);\n            _pairs = alloc_bucket((size_type)(rhs._num_buckets * rhs.max_load_factor()) + 4);\n        }\n\n        clone(rhs);\n        return *this;\n    }\n\n    HashMap& operator=(HashMap&& rhs) noexcept\n    {\n        if (this != &rhs) {\n            swap(rhs);\n            rhs.clear();\n        }\n        return *this;\n    }\n\n    template<typename Con>\n    bool operator == (const Con& rhs) const\n    {\n        if (size() != rhs.size())\n            return false;\n\n        for (auto it = begin(), last = end(); it != last; ++it) {\n            auto oi = rhs.find(it->first);\n            if (oi == rhs.end() || it->second != oi->second)\n                return false;\n        }\n        return true;\n    }\n\n    template<typename Con>\n    bool operator != (const Con& rhs) const { return !(*this == rhs); }\n\n    ~HashMap() noexcept\n    {\n        clearkv();\n        free(_pairs);\n        free(_index);\n        _index = nullptr;\n        _pairs = nullptr;\n    }\n\n    void clone(const HashMap& rhs)\n    {\n        _hasher      = rhs._hasher;\n//        _eq          = rhs._eq;\n        _num_buckets = rhs._num_buckets;\n        _num_filled  = rhs._num_filled;\n        _mlf         = rhs._mlf;\n        _last        = rhs._last;\n        _mask        = rhs._mask;\n#if EMH_HIGH_LOAD\n        _ehead       = rhs._ehead;\n#endif\n        _etail       = rhs._etail;\n\n        auto opairs  = rhs._pairs;\n        memcpy((char*)_index, (char*)rhs._index, (_num_buckets + EAD) * sizeof(Index));\n\n        if (is_copy_trivially()) {\n            memcpy((char*)_pairs, (char*)opairs, _num_filled * sizeof(value_type));\n        } else {\n            for (size_type slot = 0; slot < _num_filled; slot++)\n                new(_pairs + slot) value_type(opairs[slot]);\n        }\n    }\n\n    void swap(HashMap& rhs)\n    {\n        //      std::swap(_eq, rhs._eq);\n        std::swap(_hasher, rhs._hasher);\n        std::swap(_pairs, rhs._pairs);\n        std::swap(_index, rhs._index);\n        std::swap(_num_buckets, rhs._num_buckets);\n        std::swap(_num_filled, rhs._num_filled);\n        std::swap(_mask, rhs._mask);\n        std::swap(_mlf, rhs._mlf);\n        std::swap(_last, rhs._last);\n#if EMH_HIGH_LOAD\n        std::swap(_ehead, rhs._ehead);\n#endif\n        std::swap(_etail, rhs._etail);\n    }\n\n    // -------------------------------------------------------------\n    iterator first() const { return {this, 0}; }\n    iterator last() const { return {this, _num_filled - 1}; }\n\n    value_type& front() { return _pairs[0]; }\n    const value_type& front() const { return _pairs[0]; }\n    value_type& back() { return _pairs[_num_filled - 1]; }\n    const value_type& back() const { return _pairs[_num_filled - 1]; }\n\n    void pop_front() { erase(begin()); } //TODO. only erase first without move last\n    void pop_back() { erase(last()); }\n\n    iterator begin() { return first(); }\n    const_iterator cbegin() const { return first(); }\n    const_iterator begin() const { return first(); }\n\n    iterator end() { return {this, _num_filled}; }\n    const_iterator cend() const { return {this, _num_filled}; }\n    const_iterator end() const { return cend(); }\n\n    const value_type* values() const { return _pairs; }\n    const Index* index() const { return _index; }\n\n    size_type size() const { return _num_filled; }\n    bool empty() const { return _num_filled == 0; }\n    size_type bucket_count() const { return _num_buckets; }\n\n    /// Returns average number of elements per bucket.\n    float load_factor() const { return static_cast<float>(_num_filled) / (_mask + 1); }\n\n    HashT& hash_function() const { return _hasher; }\n    EqT& key_eq() const { return _eq; }\n\n    void max_load_factor(float mlf)\n    {\n        if (mlf < 0.992 && mlf > EMH_MIN_LOAD_FACTOR) {\n            _mlf = (uint32_t)((1 << 27) / mlf);\n            if (_num_buckets > 0) rehash(_num_buckets);\n        }\n    }\n\n    constexpr float max_load_factor() const { return (1 << 27) / (float)_mlf; }\n    constexpr size_type max_size() const { return (1ull << (sizeof(size_type) * 8 - 1)); }\n    constexpr size_type max_bucket_count() const { return max_size(); }\n\n#if EMH_STATIS\n    //Returns the bucket number where the element with key k is located.\n    size_type bucket(const KeyT& key) const\n    {\n        const auto bucket = hash_bucket(key);\n        const auto next_bucket = _index[bucket].next;\n        if ((int)next_bucket < 0)\n            return 0;\n        else if (bucket == next_bucket)\n            return bucket + 1;\n\n        return hash_main(bucket) + 1;\n    }\n\n    //Returns the number of elements in bucket n.\n    size_type bucket_size(const size_type bucket) const\n    {\n        auto next_bucket = _index[bucket].next;\n        if ((int)next_bucket < 0)\n            return 0;\n\n        next_bucket = hash_main(bucket);\n        size_type ibucket_size = 1;\n\n        //iterator each item in current main bucket\n        while (true) {\n            const auto nbucket = _index[next_bucket].next;\n            if (nbucket == next_bucket) {\n                break;\n            }\n            ibucket_size ++;\n            next_bucket = nbucket;\n        }\n        return ibucket_size;\n    }\n\n    size_type get_main_bucket(const size_type bucket) const\n    {\n        auto next_bucket = _index[bucket].next;\n        if ((int)next_bucket < 0)\n            return INACTIVE;\n\n        return hash_main(bucket);\n    }\n\n    size_type get_diss(size_type bucket, size_type next_bucket, const size_type slots) const\n    {\n        auto pbucket = reinterpret_cast<uint64_t>(&_pairs[bucket]);\n        auto pnext   = reinterpret_cast<uint64_t>(&_pairs[next_bucket]);\n        if (pbucket / EMH_CACHE_LINE_SIZE == pnext / EMH_CACHE_LINE_SIZE)\n            return 0;\n        size_type diff = pbucket > pnext ? (pbucket - pnext) : (pnext - pbucket);\n        if (diff / EMH_CACHE_LINE_SIZE < slots - 1)\n            return diff / EMH_CACHE_LINE_SIZE + 1;\n        return slots - 1;\n    }\n\n    int get_bucket_info(const size_type bucket, size_type steps[], const size_type slots) const\n    {\n        auto next_bucket = _index[bucket].next;\n        if ((int)next_bucket < 0)\n            return -1;\n\n        const auto main_bucket = hash_main(bucket);\n        if (next_bucket == main_bucket)\n            return 1;\n        else if (main_bucket != bucket)\n            return 0;\n\n        steps[get_diss(bucket, next_bucket, slots)] ++;\n        size_type ibucket_size = 2;\n        //find a empty and linked it to tail\n        while (true) {\n            const auto nbucket = _index[next_bucket].next;\n            if (nbucket == next_bucket)\n                break;\n\n            steps[get_diss(nbucket, next_bucket, slots)] ++;\n            ibucket_size ++;\n            next_bucket = nbucket;\n        }\n        return (int)ibucket_size;\n    }\n\n    void dump_statics() const\n    {\n        const size_type slots = 128;\n        size_type buckets[slots + 1] = {0};\n        size_type steps[slots + 1]   = {0};\n        for (size_type bucket = 0; bucket < _num_buckets; ++bucket) {\n            auto bsize = get_bucket_info(bucket, steps, slots);\n            if (bsize > 0)\n                buckets[bsize] ++;\n        }\n\n        size_type sumb = 0, collision = 0, sumc = 0, finds = 0, sumn = 0;\n        puts(\"============== buckets size ration =========\");\n        for (size_type i = 0; i < sizeof(buckets) / sizeof(buckets[0]); i++) {\n            const auto bucketsi = buckets[i];\n            if (bucketsi == 0)\n                continue;\n            sumb += bucketsi;\n            sumn += bucketsi * i;\n            collision += bucketsi * (i - 1);\n            finds += bucketsi * i * (i + 1) / 2;\n            printf(\"  %2u  %8u  %2.2lf|  %.2lf\\n\", i, bucketsi, bucketsi * 100.0 * i / _num_filled, sumn * 100.0 / _num_filled);\n        }\n\n        puts(\"========== collision miss ration ===========\");\n        for (size_type i = 0; i < sizeof(steps) / sizeof(steps[0]); i++) {\n            sumc += steps[i];\n            if (steps[i] <= 2)\n                continue;\n            printf(\"  %2u  %8u  %.2lf  %.2lf\\n\", i, steps[i], steps[i] * 100.0 / collision, sumc * 100.0 / collision);\n        }\n\n        if (sumb == 0)  return;\n        printf(\"    _num_filled/bucket_size/packed collision/cache_miss/hit_find = %u/%.2lf/%zd/ %.2lf%%/%.2lf%%/%.2lf\\n\",\n                _num_filled, _num_filled * 1.0 / sumb, sizeof(value_type), (collision * 100.0 / _num_filled), (collision - steps[0]) * 100.0 / _num_filled, finds * 1.0 / _num_filled);\n        assert(sumn == _num_filled);\n        assert(sumc == collision);\n        puts(\"============== buckets size end =============\");\n    }\n#endif\n\n    void pack_zero(ValueT zero)\n    {\n        _pairs[_num_filled] = {KeyT(), zero};\n    }\n\n    // ------------------------------------------------------------\n    template<typename K=KeyT>\n    iterator find(const K& key) noexcept\n    {\n        return {this, find_filled_slot(key)};\n    }\n\n    template<typename K=KeyT>\n    const_iterator find(const K& key) const noexcept\n    {\n        return {this, find_filled_slot(key)};\n    }\n\n    template<typename K=KeyT>\n    ValueT& at(const K& key)\n    {\n        const auto slot = find_filled_slot(key);\n        //throw\n        return _pairs[slot].second;\n    }\n\n    template<typename K=KeyT>\n    const ValueT& at(const K& key) const\n    {\n        const auto slot = find_filled_slot(key);\n        //throw\n        return _pairs[slot].second;\n    }\n\n    const ValueT& index(const uint32_t index) const\n    {\n        return _pairs[index].second;\n    }\n\n    ValueT& index(const uint32_t index)\n    {\n        return _pairs[index].second;\n    }\n\n    template<typename K=KeyT>\n    bool contains(const K& key) const noexcept\n    {\n        return find_filled_slot(key) != _num_filled;\n    }\n\n    template<typename K=KeyT>\n    size_type count(const K& key) const noexcept\n    {\n        return find_filled_slot(key) == _num_filled ? 0 : 1;\n        //return find_sorted_bucket(key) == END ? 0 : 1;\n        //return find_hash_bucket(key) == END ? 0 : 1;\n    }\n\n    template<typename K=KeyT>\n    std::pair<iterator, iterator> equal_range(const K& key)\n    {\n        const auto found = find(key);\n        if (found.second == _num_filled)\n            return { found, found };\n        else\n            return { found, std::next(found) };\n    }\n\n    void merge(HashMap& rhs)\n    {\n        if (empty()) {\n            *this = std::move(rhs);\n            return;\n        }\n\n        for (auto rit = rhs.begin(); rit != rhs.end(); ) {\n            auto fit = find(rit->first);\n            if (fit == end()) {\n                insert_unique(rit->first, std::move(rit->second));\n                rit = rhs.erase(rit);\n            } else {\n                ++rit;\n            }\n        }\n    }\n\n    /// Returns the matching ValueT or nullptr if k isn't found.\n    bool try_get(const KeyT& key, ValueT& val) const noexcept\n    {\n        const auto slot = find_filled_slot(key);\n        const auto found = slot != _num_filled;\n        if (found) {\n            val = _pairs[slot].second;\n        }\n        return found;\n    }\n\n    /// Returns the matching ValueT or nullptr if k isn't found.\n    ValueT* try_get(const KeyT& key) noexcept\n    {\n        const auto slot = find_filled_slot(key);\n        return slot != _num_filled ? &_pairs[slot].second : nullptr;\n    }\n\n    /// Const version of the above\n    ValueT* try_get(const KeyT& key) const noexcept\n    {\n        const auto slot = find_filled_slot(key);\n        return slot != _num_filled ? &_pairs[slot].second : nullptr;\n    }\n\n    /// set value if key exist\n    bool try_set(const KeyT& key, const ValueT& val) noexcept\n    {\n        const auto slot = find_filled_slot(key);\n        if (slot == _num_filled)\n            return false;\n\n        _pairs[slot].second = val;\n        return true;\n    }\n\n    /// set value if key exist\n    bool try_set(const KeyT& key, ValueT&& val) noexcept\n    {\n        const auto slot = find_filled_slot(key);\n        if (slot == _num_filled)\n            return false;\n\n        _pairs[slot].second = std::move(val);\n        return true;\n    }\n\n    /// Convenience function.\n    ValueT get_or_return_default(const KeyT& key) const noexcept\n    {\n        const auto slot = find_filled_slot(key);\n        return slot == _num_filled ? ValueT() : _pairs[slot].second;\n    }\n\n    // -----------------------------------------------------\n    std::pair<iterator, bool> do_insert(const value_type& value) noexcept\n    {\n        const auto key_hash = hash_key(value.first);\n        const auto bucket = find_or_allocate(value.first, key_hash);\n        const auto bempty = EMH_EMPTY(bucket);\n        if (bempty) {\n            EMH_NEW(value.first, value.second, bucket, key_hash);\n        }\n\n        const auto slot = _index[bucket].slot & _mask;\n        return { {this, slot}, bempty };\n    }\n\n    std::pair<iterator, bool> do_insert(value_type&& value) noexcept\n    {\n        const auto key_hash = hash_key(value.first);\n        const auto bucket = find_or_allocate(value.first, key_hash);\n        const auto bempty = EMH_EMPTY(bucket);\n        if (bempty) {\n            EMH_NEW(std::move(value.first), std::move(value.second), bucket, key_hash);\n        }\n\n        const auto slot = _index[bucket].slot & _mask;\n        return { {this, slot}, bempty };\n    }\n\n    template<typename K, typename V>\n    std::pair<iterator, bool> do_insert(K&& key, V&& val) noexcept\n    {\n        const auto key_hash = hash_key(key);\n        const auto bucket = find_or_allocate(key, key_hash);\n        const auto bempty = EMH_EMPTY(bucket);\n        if (bempty) {\n            EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);\n        }\n\n        const auto slot = _index[bucket].slot & _mask;\n        return { {this, slot}, bempty };\n    }\n\n    template<typename K, typename V>\n    std::pair<iterator, bool> do_assign(K&& key, V&& val) noexcept\n    {\n        check_expand_need();\n        const auto key_hash = hash_key(key);\n        const auto bucket = find_or_allocate(key, key_hash);\n        const auto bempty = EMH_EMPTY(bucket);\n        if (bempty) {\n            EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);\n        } else {\n            _pairs[_index[bucket].slot & _mask].second = std::move(val);\n        }\n\n        const auto slot = _index[bucket].slot & _mask;\n        return { {this, slot}, bempty };\n    }\n\n    std::pair<iterator, bool> insert(const value_type& p)\n    {\n        check_expand_need();\n        return do_insert(p);\n    }\n\n    std::pair<iterator, bool> insert(value_type && p)\n    {\n        check_expand_need();\n        return do_insert(std::move(p));\n    }\n\n    void insert(std::initializer_list<value_type> ilist)\n    {\n        reserve(ilist.size() + _num_filled, false);\n        for (auto it = ilist.begin(); it != ilist.end(); ++it)\n            do_insert(*it);\n    }\n\n    template <typename Iter>\n    void insert(Iter first, Iter last)\n    {\n        reserve(std::distance(first, last) + _num_filled, false);\n        for (; first != last; ++first)\n            do_insert(first->first, first->second);\n    }\n\n#if 0\n    template <typename Iter>\n    void insert_unique(Iter begin, Iter end)\n    {\n        reserve(std::distance(begin, end) + _num_filled, false);\n        for (; begin != end; ++begin) {\n            insert_unique(*begin);\n        }\n    }\n#endif\n\n    template<typename K, typename V>\n    size_type insert_unique(K&& key, V&& val)\n    {\n        check_expand_need();\n        const auto key_hash = hash_key(key);\n        auto bucket = find_unique_bucket(key_hash);\n        EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);\n        return bucket;\n    }\n\n    size_type insert_unique(value_type&& value)\n    {\n        return insert_unique(std::move(value.first), std::move(value.second));\n    }\n\n    size_type insert_unique(const value_type& value)\n    {\n        return insert_unique(value.first, value.second);\n    }\n\n    template <class... Args>\n    std::pair<iterator, bool> emplace(Args&&... args) noexcept\n    {\n        check_expand_need();\n        return do_insert(std::forward<Args>(args)...);\n    }\n\n    //no any optimize for position\n    template <class... Args>\n    iterator emplace_hint(const_iterator hint, Args&&... args)\n    {\n        (void)hint;\n        check_expand_need();\n        return do_insert(std::forward<Args>(args)...).first;\n    }\n\n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(const KeyT& k, Args&&... args)\n    {\n        check_expand_need();\n        return do_insert(k, std::forward<Args>(args)...);\n    }\n\n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(KeyT&& k, Args&&... args)\n    {\n        check_expand_need();\n        return do_insert(std::move(k), std::forward<Args>(args)...);\n    }\n\n    template <class... Args>\n    size_type emplace_unique(Args&&... args)\n    {\n        return insert_unique(std::forward<Args>(args)...);\n    }\n\n    std::pair<iterator, bool> insert_or_assign(const KeyT& key, ValueT&& val) { return do_assign(key, std::forward<ValueT>(val)); }\n    std::pair<iterator, bool> insert_or_assign(KeyT&& key, ValueT&& val) { return do_assign(std::move(key), std::forward<ValueT>(val)); }\n\n    /// Return the old value or ValueT() if it didn't exist.\n    ValueT set_get(const KeyT& key, const ValueT& val)\n    {\n        check_expand_need();\n        const auto key_hash = hash_key(key);\n        const auto bucket = find_or_allocate(key, key_hash);\n        if (EMH_EMPTY(bucket)) {\n            EMH_NEW(key, val, bucket, key_hash);\n            return ValueT();\n        } else {\n            const auto slot = _index[bucket].slot & _mask;\n            ValueT old_value(val);\n            std::swap(_pairs[slot].second, old_value);\n            return old_value;\n        }\n    }\n\n    /// Like std::map<KeyT, ValueT>::operator[].\n    ValueT& operator[](const KeyT& key) noexcept\n    {\n        check_expand_need();\n        const auto key_hash = hash_key(key);\n        const auto bucket = find_or_allocate(key, key_hash);\n        if (EMH_EMPTY(bucket)) {\n            /* Check if inserting a value rather than overwriting an old entry */\n            EMH_NEW(key, std::move(ValueT()), bucket, key_hash);\n        }\n\n        const auto slot = _index[bucket].slot & _mask;\n        return _pairs[slot].second;\n    }\n\n    ValueT& operator[](KeyT&& key) noexcept\n    {\n        check_expand_need();\n        const auto key_hash = hash_key(key);\n        const auto bucket = find_or_allocate(key, key_hash);\n        if (EMH_EMPTY(bucket)) {\n            EMH_NEW(std::move(key), std::move(ValueT()), bucket, key_hash);\n        }\n\n        const auto slot = _index[bucket].slot & _mask;\n        return _pairs[slot].second;\n    }\n\n    /// Erase an element from the hash table.\n    /// return 0 if element was not found\n    size_type erase(const KeyT& key) noexcept\n    {\n        const auto key_hash = hash_key(key);\n        const auto sbucket = find_filled_bucket(key, key_hash);\n        if (sbucket == INACTIVE)\n            return 0;\n\n        const auto main_bucket = key_hash & _mask;\n        erase_slot(sbucket, (size_type)main_bucket);\n        return 1;\n    }\n\n    //iterator erase(const_iterator begin_it, const_iterator end_it)\n    iterator erase(const const_iterator& cit) noexcept\n    {\n        const auto slot = (size_type)(cit.kv_ - _pairs);\n        size_type main_bucket;\n        const auto sbucket = find_slot_bucket(slot, main_bucket); //TODO\n        erase_slot(sbucket, main_bucket);\n        return {this, slot};\n    }\n\n    //only last >= first\n    iterator erase(const_iterator first, const_iterator last) noexcept\n    {\n        auto esize = long(last.kv_ - first.kv_);\n        auto tsize = long((_pairs + _num_filled) - last.kv_); //last to tail size\n        auto next = first;\n        while (tsize -- > 0) {\n            if (esize-- <= 0)\n                break;\n            next = ++erase(next);\n        }\n\n        //fast erase from last\n        next = this->last();\n        while (esize -- > 0)\n            next = --erase(next);\n\n        return {this, size_type(next.kv_ - _pairs)};\n    }\n\n    template<typename Pred>\n    size_type erase_if(Pred pred)\n    {\n        auto old_size = size();\n        for (auto it = begin(); it != end();) {\n            if (pred(*it))\n                it = erase(it);\n            else\n                ++it;\n        }\n        return old_size - size();\n    }\n\n    static constexpr bool is_triviall_destructable()\n    {\n#if __cplusplus >= 201402L || _MSC_VER > 1600\n        return !(std::is_trivially_destructible<KeyT>::value && std::is_trivially_destructible<ValueT>::value);\n#else\n        return !(std::is_pod<KeyT>::value && std::is_pod<ValueT>::value);\n#endif\n    }\n\n    static constexpr bool is_copy_trivially()\n    {\n#if __cplusplus >= 201103L || _MSC_VER > 1600\n        return (std::is_trivially_copyable<KeyT>::value && std::is_trivially_copyable<ValueT>::value);\n#else\n        return (std::is_pod<KeyT>::value && std::is_pod<ValueT>::value);\n#endif\n    }\n\n    void clearkv()\n    {\n        if (is_triviall_destructable()) {\n            while (_num_filled --)\n                _pairs[_num_filled].~value_type();\n        }\n    }\n\n    /// Remove all elements, keeping full capacity.\n    void clear() noexcept\n    {\n        clearkv();\n\n        if (_num_filled > 0)\n            memset((char*)_index, INACTIVE, sizeof(_index[0]) * _num_buckets);\n\n        _last = _num_filled = 0;\n        _etail = INACTIVE;\n\n#if EMH_HIGH_LOAD\n        _ehead = 0;\n#endif\n    }\n\n    void shrink_to_fit(const float min_factor = EMH_DEFAULT_LOAD_FACTOR / 4)\n    {\n        if (load_factor() < min_factor && bucket_count() > 10) //safe guard\n            rehash(_num_filled + 1);\n    }\n\n#if EMH_HIGH_LOAD\n    #define EMH_PREVET(i, n) i[n].slot\n    void set_empty()\n    {\n        auto prev = 0;\n        for (int32_t bucket = 1; bucket < _num_buckets; ++bucket) {\n            if (EMH_EMPTY(bucket)) {\n                if (prev != 0) {\n                    EMH_PREVET(_index, bucket) = prev;\n                    _index[_prev].next = -bucket;\n                }\n                else\n                    _ehead = bucket;\n                prev = bucket;\n            }\n        }\n\n        EMH_PREVET(_index, _ehead) = prev;\n        _index[_prev].next = 0-_ehead;\n        _ehead = 0-_index[_ehead].next;\n    }\n\n    void clear_empty()\n    {\n        auto prev = EMH_PREVET(_index, _ehead);\n        while (prev != _ehead) {\n            _index[_prev].next = INACTIVE;\n            prev = EMH_PREVET(_index, prev);\n        }\n        _index[_ehead].next = INACTIVE;\n        _ehead = 0;\n    }\n\n    //prev-ehead->next\n    size_type pop_empty(const size_type bucket)\n    {\n        const auto prev_bucket = EMH_PREVET(_index, bucket);\n        const int next_bucket = 0-_index[bucket].next;\n\n        EMH_PREVET(_index, next_bucket) = prev_bucket;\n        _index[prev_bucket].next = -next_bucket;\n\n        _ehead = next_bucket;\n        return bucket;\n    }\n\n    //ehead->bucket->next\n    void push_empty(const int32_t bucket)\n    {\n        const int next_bucket = 0-_index[_ehead].next;\n        assert(next_bucket > 0);\n\n        EMH_PREVET(_index, bucket) = _ehead;\n        _index[bucket].next = -next_bucket;\n\n        EMH_PREVET(_index, next_bucket) = bucket;\n        _index[_ehead].next = -bucket;\n        //        _ehead = bucket;\n    }\n#endif\n\n    /// Make room for this many elements\n    bool reserve(uint64_t num_elems, bool force)\n    {\n        (void)force;\n#if EMH_HIGH_LOAD == 0\n        const auto required_buckets = num_elems * _mlf >> 27;\n        if (EMH_LIKELY(required_buckets < _mask)) // && !force\n            return false;\n\n#elif EMH_HIGH_LOAD\n        const auto required_buckets = num_elems + num_elems * 1 / 9;\n        if (EMH_LIKELY(required_buckets < _mask))\n            return false;\n\n        else if (_num_buckets < 16 && _num_filled < _num_buckets)\n            return false;\n\n        else if (_num_buckets > EMH_HIGH_LOAD) {\n            if (_ehead == 0) {\n                set_empty();\n                return false;\n            } else if (/*_num_filled + 100 < _num_buckets && */_index[_ehead].next != 0-_ehead) {\n                return false;\n            }\n        }\n#endif\n#if EMH_STATIS\n        if (_num_filled > EMH_STATIS) dump_statics();\n#endif\n\n        //assert(required_buckets < max_size());\n        rehash(required_buckets + 2);\n        return true;\n    }\n\n    static value_type* alloc_bucket(size_type num_buckets)\n    {\n#ifdef EMH_ALLOC\n        auto new_pairs = aligned_alloc(32, (uint64_t)num_buckets * sizeof(value_type));\n#else\n        auto new_pairs = malloc((uint64_t)num_buckets * sizeof(value_type));\n#endif\n        return (value_type *)(new_pairs);\n    }\n\n    static Index* alloc_index(size_type num_buckets)\n    {\n        auto new_index = (char*)malloc((uint64_t)(EAD + num_buckets) * sizeof(Index));\n        return (Index *)(new_index);\n    }\n\n    bool reserve(size_type required_buckets) noexcept\n    {\n        if (_num_filled != required_buckets)\n            return reserve(required_buckets, true);\n\n        _last = 0;\n#if EMH_HIGH_LOAD\n        _ehead = 0;\n#endif\n\n#if EMH_SORT\n        std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) {\n            const auto hashl = (size_type)hash_key(l.first) & _mask, hashr = (size_type)hash_key(r.first) & _mask;\n            return hashl < hashr;\n            //return l.first < r.first;\n        });\n#endif\n\n        memset((char*)_index, INACTIVE, sizeof(_index[0]) * _num_buckets);\n        for (size_type slot = 0; slot < _num_filled; slot++) {\n            const auto& key = _pairs[slot].first;\n            const auto key_hash = hash_key(key);\n            const auto bucket = size_type(key_hash & _mask);\n            auto& next_bucket = _index[bucket].next;\n            if ((int)next_bucket < 0)\n                _index[bucket] = {1, slot | ((size_type)(key_hash) & ~_mask)};\n            else {\n                _index[bucket].slot |= (size_type)(key_hash) & ~_mask;\n                next_bucket ++;\n            }\n        }\n        return true;\n    }\n\n    void rebuild(size_type num_buckets) noexcept\n    {\n        free(_index);\n        auto new_pairs = (value_type*)alloc_bucket((size_type)(num_buckets * max_load_factor()) + 4);\n        if (is_copy_trivially()) {\n            if (_pairs)\n            memcpy((char*)new_pairs, (char*)_pairs, _num_filled * sizeof(value_type));\n        } else {\n            for (size_type slot = 0; slot < _num_filled; slot++) {\n                new(new_pairs + slot) value_type(std::move(_pairs[slot]));\n                if (is_triviall_destructable())\n                    _pairs[slot].~value_type();\n            }\n        }\n        free(_pairs);\n        _pairs = new_pairs;\n        _index = (Index*)alloc_index (num_buckets);\n\n        memset((char*)_index, INACTIVE, sizeof(_index[0]) * num_buckets);\n        memset((char*)(_index + num_buckets), 0, sizeof(_index[0]) * EAD);\n    }\n\n    void rehash(uint64_t required_buckets)\n    {\n        if (required_buckets < _num_filled)\n            return;\n\n        assert(required_buckets < max_size());\n        auto num_buckets = _num_filled > (1u << 16) ? (1u << 16) : 4u;\n        while (num_buckets < required_buckets) { num_buckets *= 2; }\n#if EMH_SAVE_MEM\n        if (sizeof(KeyT) < sizeof(size_type) && num_buckets >= (1ul << (2 * 8)))\n            num_buckets = 2ul << (sizeof(KeyT) * 8);\n#endif\n\n#if EMH_REHASH_LOG\n        auto last = _last;\n        size_type collision = 0;\n#endif\n\n#if EMH_HIGH_LOAD\n        _ehead = 0;\n#endif\n        _last = 0;\n\n        _mask        = num_buckets - 1;\n#if EMH_PACK_TAIL > 1\n        _last = _mask;\n        num_buckets += num_buckets * EMH_PACK_TAIL / 100; //add more 5-10%\n#endif\n        _num_buckets = num_buckets;\n\n        rebuild(num_buckets);\n\n#ifdef EMH_SORT\n        std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) {\n            const auto hashl = hash_key(l.first), hashr = hash_key(r.first);\n            auto diff = int64_t((hashl & _mask) - (hashr & _mask));\n            if (diff != 0)\n                return diff < 0;\n            return hashl < hashr;\n//          return l.first < r.first;\n        });\n#endif\n\n        _etail = INACTIVE;\n        for (size_type slot = 0; slot < _num_filled; ++slot) {\n            const auto& key = _pairs[slot].first;\n            const auto key_hash = hash_key(key);\n            const auto bucket = find_unique_bucket(key_hash);\n            _index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~_mask) };\n\n#if EMH_REHASH_LOG\n            if (bucket != hash_main(bucket))\n                collision ++;\n#endif\n        }\n\n#if EMH_REHASH_LOG\n        if (_num_filled > EMH_REHASH_LOG) {\n            auto mbucket = _num_filled - collision;\n            char buff[255] = {0};\n            sprintf(buff, \"    _num_filled/aver_size/K.V/pack/collision|last = %u/%.2lf/%s.%s/%zd|%.2lf%%,%.2lf%%\",\n                    _num_filled, double (_num_filled) / mbucket, typeid(KeyT).name(), typeid(ValueT).name(), sizeof(_pairs[0]), collision * 100.0 / _num_filled, last * 100.0 / _num_buckets);\n#ifdef EMH_LOG\n            static uint32_t ihashs = 0; EMH_LOG() << \"hash_nums = \" << ihashs ++ << \"|\" <<__FUNCTION__ << \"|\" << buff << endl;\n#else\n            puts(buff);\n#endif\n        }\n#endif\n    }\n\nprivate:\n    // Can we fit another element?\n    bool check_expand_need()\n    {\n        return reserve(_num_filled, false);\n    }\n\n    static void prefetch_heap_block(char* ctrl)\n    {\n        // Prefetch the heap-allocated memory region to resolve potential TLB\n        // misses.  This is intended to overlap with execution of calculating the hash for a key.\n#if __linux__\n        __builtin_prefetch(static_cast<const void*>(ctrl));\n#elif _WIN32 && defined(_M_IX86)\n        _mm_prefetch((const char*)ctrl, _MM_HINT_T0);\n#endif\n    }\n\n    size_type slot_to_bucket(const size_type slot) const noexcept\n    {\n        size_type main_bucket;\n        return find_slot_bucket(slot, main_bucket); //TODO\n    }\n\n    //very slow\n    void erase_slot(const size_type sbucket, const size_type main_bucket) noexcept\n    {\n        const auto slot = _index[sbucket].slot & _mask;\n        const auto ebucket = erase_bucket(sbucket, main_bucket);\n        const auto last_slot = --_num_filled;\n        if (EMH_LIKELY(slot != last_slot)) {\n            const auto last_bucket = (_etail == INACTIVE || ebucket == _etail)\n                ? slot_to_bucket(last_slot) : _etail;\n\n            _pairs[slot] = std::move(_pairs[last_slot]);\n            _index[last_bucket].slot = slot | (_index[last_bucket].slot & ~_mask);\n        }\n\n        if (is_triviall_destructable())\n            _pairs[last_slot].~value_type();\n\n        _etail = INACTIVE;\n        _index[ebucket] = {INACTIVE, 0};\n#if EMH_HIGH_LOAD\n        if (_ehead) {\n            if (10 * _num_filled < 8 * _num_buckets)\n                clear_empty();\n            else if (ebucket)\n                push_empty(ebucket);\n        }\n#endif\n    }\n\n    size_type erase_bucket(const size_type bucket, const size_type main_bucket) noexcept\n    {\n        const auto next_bucket = _index[bucket].next;\n        if (bucket == main_bucket) {\n            if (main_bucket != next_bucket) {\n                const auto nbucket = _index[next_bucket].next;\n                _index[main_bucket] = {\n                    (nbucket == next_bucket) ? main_bucket : nbucket,\n                    _index[next_bucket].slot\n                };\n            }\n            return next_bucket;\n        }\n\n        const auto prev_bucket = find_prev_bucket(main_bucket, bucket);\n        _index[prev_bucket].next = (bucket == next_bucket) ? prev_bucket : next_bucket;\n        return bucket;\n    }\n\n    // Find the slot with this key, or return bucket size\n    size_type find_slot_bucket(const size_type slot, size_type& main_bucket) const\n    {\n        const auto key_hash = hash_key(_pairs[slot].first);\n        const auto bucket = main_bucket = size_type(key_hash & _mask);\n        if (slot == (_index[bucket].slot & _mask))\n            return bucket;\n\n        auto next_bucket = _index[bucket].next;\n        while (true) {\n            if (EMH_LIKELY(slot == (_index[next_bucket].slot & _mask)))\n                return next_bucket;\n            next_bucket = _index[next_bucket].next;\n        }\n\n        return INACTIVE;\n    }\n\n    // Find the slot with this key, or return bucket size\n    size_type find_filled_bucket(const KeyT& key, uint64_t key_hash) const noexcept\n    {\n        const auto bucket = size_type(key_hash & _mask);\n        auto next_bucket  = _index[bucket].next;\n        if (EMH_UNLIKELY((int)next_bucket < 0))\n            return INACTIVE;\n\n        const auto slot = _index[bucket].slot & _mask;\n        //prefetch_heap_block((char*)&_pairs[slot]);\n        if (EMH_EQHASH(bucket, key_hash)) {\n            if (EMH_LIKELY(_eq(key, _pairs[slot].first)))\n                return bucket;\n        }\n        if (next_bucket == bucket)\n            return INACTIVE;\n\n        while (true) {\n            if (EMH_EQHASH(next_bucket, key_hash)) {\n                const auto next_slot = _index[next_bucket].slot & _mask;\n                if (EMH_LIKELY(_eq(key, _pairs[next_slot].first)))\n                    return next_bucket;\n            }\n\n            const auto nbucket = _index[next_bucket].next;\n            if (nbucket == next_bucket)\n                return INACTIVE;\n            next_bucket = nbucket;\n        }\n\n        return INACTIVE;\n    }\n\n    // Find the slot with this key, or return bucket size\n    template<typename K=KeyT>\n    size_type find_filled_slot(const K& key) const noexcept\n    {\n        const auto key_hash = hash_key(key);\n        const auto bucket = size_type(key_hash & _mask);\n        auto next_bucket = _index[bucket].next;\n        if ((int)next_bucket < 0)\n            return _num_filled;\n\n        const auto slot = _index[bucket].slot & _mask;\n        //prefetch_heap_block((char*)&_pairs[slot]);\n        if (EMH_EQHASH(bucket, key_hash)) {\n            if (EMH_LIKELY(_eq(key, _pairs[slot].first)))\n                return slot;\n        }\n        if (next_bucket == bucket)\n            return _num_filled;\n\n        while (true) {\n            if (EMH_EQHASH(next_bucket, key_hash)) {\n                const auto next_slot = _index[next_bucket].slot & _mask;\n                if (EMH_LIKELY(_eq(key, _pairs[next_slot].first)))\n                    return next_slot;\n            }\n\n            const auto nbucket = _index[next_bucket].next;\n            if (nbucket == next_bucket)\n                return _num_filled;\n            next_bucket = nbucket;\n        }\n\n        return _num_filled;\n    }\n\n#if EMH_SORT\n    size_type find_hash_bucket(const KeyT& key) const noexcept\n    {\n        const auto key_hash = hash_key(key);\n        const auto bucket = size_type(key_hash & _mask);\n        const auto next_bucket = _index[bucket].next;\n        if ((int)next_bucket < 0)\n            return END;\n\n        auto slot = _index[bucket].slot & _mask;\n        if (_eq(key, _pairs[slot++].first))\n            return slot;\n        else if (next_bucket == bucket)\n            return END;\n\n        while (true) {\n            const auto& okey = _pairs[slot++].first;\n            if (_eq(key, okey))\n                return slot;\n\n            const auto hasho = hash_key(okey);\n            if ((hasho & _mask) != bucket)\n                break;\n            else if (hasho > key_hash)\n                break;\n            else if (EMH_UNLIKELY(slot >= _num_filled))\n                break;\n        }\n\n        return END;\n    }\n\n    //only for find/can not insert\n    size_type find_sorted_bucket(const KeyT& key) const noexcept\n    {\n        const auto key_hash = hash_key(key);\n        const auto bucket = size_type(key_hash & _mask);\n        const auto slots = (int)(_index[bucket].next); //TODO\n        if (slots < 0 /**|| key < _pairs[slot].first*/)\n            return END;\n\n        const auto slot = _index[bucket].slot & _mask;\n        auto ormask = _index[bucket].slot & ~_mask;\n        auto hmask  = (size_type)(key_hash) & ~_mask;\n        if ((hmask | ormask) != ormask)\n            return END;\n\n        if (_eq(key, _pairs[slot].first))\n            return slot;\n        else if (slots == 1 || key < _pairs[slot].first)\n            return END;\n\n#if EMH_SORT\n        if (key < _pairs[slot].first || key > _pairs[slots + slot - 1].first)\n            return END;\n#endif\n\n        for (size_type i = 1; i < slots; ++i) {\n            const auto& okey = _pairs[slot + i].first;\n            if (_eq(key, okey))\n                return slot + i;\n            //            else if (okey > key)\n            //                return END;\n        }\n\n        return END;\n    }\n#endif\n\n    //kick out bucket and find empty to occpuy\n    //it will break the origin link and relink again.\n    //before: main_bucket-->prev_bucket --> bucket   --> next_bucket\n    //after : main_bucket-->prev_bucket --> (removed)--> new_bucket--> next_bucket\n    size_type kickout_bucket(const size_type kmain, const size_type bucket) noexcept\n    {\n        const auto next_bucket = _index[bucket].next;\n        const auto new_bucket  = find_empty_bucket(next_bucket, 2);\n        const auto prev_bucket = find_prev_bucket(kmain, bucket);\n\n        const auto last = next_bucket == bucket ? new_bucket : next_bucket;\n        _index[new_bucket] = {last, _index[bucket].slot};\n\n        _index[prev_bucket].next = new_bucket;\n        _index[bucket].next = INACTIVE;\n\n        return bucket;\n    }\n\n    /*\n     ** inserts a new key into a hash table; first, check whether key's main\n     ** bucket/position is free. If not, check whether colliding node/bucket is in its main\n     ** position or not: if it is not, move colliding bucket to an empty place and\n     ** put new key in its main position; otherwise (colliding bucket is in its main\n     ** position), new key goes to an empty position.\n     */\n    template<typename K=KeyT>\n    size_type find_or_allocate(const K& key, uint64_t key_hash) noexcept\n    {\n        const auto bucket = size_type(key_hash & _mask);\n        auto next_bucket = _index[bucket].next;\n        prefetch_heap_block((char*)&_pairs[bucket]);\n        if ((int)next_bucket < 0) {\n#if EMH_HIGH_LOAD\n            if (next_bucket != INACTIVE)\n                pop_empty(bucket);\n#endif\n            return bucket;\n        }\n\n        const auto slot = _index[bucket].slot & _mask;\n        if (EMH_EQHASH(bucket, key_hash))\n            if (EMH_LIKELY(_eq(key, _pairs[slot].first)))\n                return bucket;\n\n        //check current bucket_key is in main bucket or not\n        const auto kmain = hash_bucket(_pairs[slot].first);\n        if (kmain != bucket)\n            return kickout_bucket(kmain, bucket);\n        else if (next_bucket == bucket)\n            return _index[next_bucket].next = find_empty_bucket(next_bucket, 1);\n\n        uint32_t csize = 1;\n        //find next linked bucket and check key\n        while (true) {\n            const auto eslot = _index[next_bucket].slot & _mask;\n            if (EMH_EQHASH(next_bucket, key_hash)) {\n                if (EMH_LIKELY(_eq(key, _pairs[eslot].first)))\n                    return next_bucket;\n            }\n\n            csize += 1;\n            const auto nbucket = _index[next_bucket].next;\n            if (nbucket == next_bucket)\n                break;\n            next_bucket = nbucket;\n        }\n\n        //find a empty and link it to tail\n        const auto new_bucket = find_empty_bucket(next_bucket, csize);\n        prefetch_heap_block((char*)&_pairs[new_bucket]);\n        return _index[next_bucket].next = new_bucket;\n    }\n\n    size_type find_unique_bucket(uint64_t key_hash) noexcept\n    {\n        const auto bucket = size_type(key_hash & _mask);\n        auto next_bucket = _index[bucket].next;\n        if ((int)next_bucket < 0) {\n#if EMH_HIGH_LOAD\n            if (next_bucket != INACTIVE)\n                pop_empty(bucket);\n#endif\n            return bucket;\n        }\n\n        //check current bucket_key is in main bucket or not\n        const auto kmain = hash_main(bucket);\n        if (EMH_UNLIKELY(kmain != bucket))\n            return kickout_bucket(kmain, bucket);\n        else if (EMH_UNLIKELY(next_bucket != bucket))\n            next_bucket = find_last_bucket(next_bucket);\n\n        return _index[next_bucket].next = find_empty_bucket(next_bucket, 2);\n    }\n\n    /***\n      Different probing techniques usually provide a trade-off between memory locality and avoidance of clustering.\n      Since Robin Hood hashing is relatively resilient to clustering (both primary and secondary), linear probing is the most cache friendly alternativeis typically used.\n\n      It's the core algorithm of this hash map with highly optimization/benchmark.\n      normally linear probing is inefficient with high load factor, it use a new 3-way linear\n      probing strategy to search empty slot. from benchmark even the load factor > 0.9, it's more 2-3 timer fast than\n      one-way search strategy.\n\n      1. linear or quadratic probing a few cache line for less cache miss from input slot \"bucket_from\".\n      2. the first  search  slot from member variant \"_last\", init with 0\n      3. the second search slot from calculated pos \"(_num_filled + _last) & _mask\", it's like a rand value\n      */\n    // key is not in this mavalue. Find a place to put it.\n    size_type find_empty_bucket(const size_type bucket_from, uint32_t csize) noexcept\n    {\n        (void)csize;\n#if EMH_HIGH_LOAD\n        if (_ehead)\n            return pop_empty(_ehead);\n#endif\n\n        auto bucket = bucket_from;\n        if (EMH_EMPTY(++bucket) || EMH_EMPTY(++bucket))\n            return bucket;\n\n#ifdef EMH_QUADRATIC\n        constexpr size_type linear_probe_length = 2 * EMH_CACHE_LINE_SIZE / sizeof(Index);//16\n        for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length; ) {\n            bucket = (bucket_from + offset) & _mask;\n            if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))\n                return bucket;\n            offset += step; //7/8. 12. 16\n        }\n#else\n        constexpr size_type quadratic_probe_length = 6u;\n        for (size_type offset = 4u, step = 3u; step < quadratic_probe_length; ) {\n            bucket = (bucket_from + offset) & _mask;\n            if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))\n                return bucket;\n            offset += step++;\n        }\n#endif\n\n#if EMH_PREFETCH\n        __builtin_prefetch(static_cast<const void*>(_index + _last + 1), 0, EMH_PREFETCH);\n#endif\n\n        for (;;) {\n#if EMH_PACK_TAIL\n            //find empty bucket and skip next\n            if (EMH_EMPTY(_last++))// || EMH_EMPTY(_last++))\n                return _last++ - 1;\n\n            if (EMH_UNLIKELY(_last >= _num_buckets))\n                _last = 0;\n\n            auto medium = (_mask / 4 + _last++) & _mask;\n            if (EMH_EMPTY(medium))\n                return medium;\n#else\n            _last &= _mask;\n            if (EMH_EMPTY(++_last))// || EMH_EMPTY(++_last))\n                return _last;\n\n            auto medium = (_num_buckets / 2 + _last) & _mask;\n            if (EMH_EMPTY(medium))// || EMH_EMPTY(++medium))\n                return medium;\n#endif\n        }\n\n        return 0;\n    }\n\n    size_type find_last_bucket(size_type main_bucket) const\n    {\n        auto next_bucket = _index[main_bucket].next;\n        if (next_bucket == main_bucket)\n            return main_bucket;\n\n        while (true) {\n            const auto nbucket = _index[next_bucket].next;\n            if (nbucket == next_bucket)\n                return next_bucket;\n            next_bucket = nbucket;\n        }\n    }\n\n    size_type find_prev_bucket(const size_type main_bucket, const size_type bucket) const\n    {\n        auto next_bucket = _index[main_bucket].next;\n        if (next_bucket == bucket)\n            return main_bucket;\n\n        while (true) {\n            const auto nbucket = _index[next_bucket].next;\n            if (nbucket == bucket)\n                return next_bucket;\n            next_bucket = nbucket;\n        }\n    }\n\n    size_type hash_bucket(const KeyT& key) const noexcept\n    {\n        return (size_type)hash_key(key) & _mask;\n    }\n\n    size_type hash_main(const size_type bucket) const noexcept\n    {\n        const auto slot = _index[bucket].slot & _mask;\n        return (size_type)hash_key(_pairs[slot].first) & _mask;\n    }\n\n#if EMH_INT_HASH\n    static constexpr uint64_t KC = UINT64_C(11400714819323198485);\n    static uint64_t hash64(uint64_t key)\n    {\n#if __SIZEOF_INT128__ && EMH_INT_HASH == 1\n        __uint128_t r = key; r *= KC;\n        return (uint64_t)(r >> 64) + (uint64_t)r;\n#elif EMH_INT_HASH == 2\n        //MurmurHash3Mixer\n        uint64_t h = key;\n        h ^= h >> 33;\n        h *= 0xff51afd7ed558ccd;\n        h ^= h >> 33;\n        h *= 0xc4ceb9fe1a85ec53;\n        h ^= h >> 33;\n        return h;\n#elif _WIN64 && EMH_INT_HASH == 1\n        uint64_t high;\n        return _umul128(key, KC, &high) + high;\n#elif EMH_INT_HASH == 3\n        auto ror  = (key >> 32) | (key << 32);\n        auto low  = key * 0xA24BAED4963EE407ull;\n        auto high = ror * 0x9FB21C651E98DF25ull;\n        auto mix  = low + high;\n        return mix;\n#elif EMH_INT_HASH == 1\n        uint64_t r = key * UINT64_C(0xca4bcaa75ec3f625);\n        return (r >> 32) + r;\n#elif EMH_WYHASH64\n        return wyhash64(key, KC);\n#else\n        uint64_t x = key;\n        x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);\n        x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);\n        x = x ^ (x >> 31);\n        return x;\n#endif\n    }\n#endif\n\n#if EMH_WYHASH_HASH\n    //#define WYHASH_CONDOM 1\n    static uint64_t wymix(uint64_t A, uint64_t B)\n    {\n#if defined(__SIZEOF_INT128__)\n        __uint128_t r = A; r *= B;\n#if WYHASH_CONDOM2\n        A ^= (uint64_t)r; B ^= (uint64_t)(r >> 64);\n#else\n        A = (uint64_t)r; B = (uint64_t)(r >> 64);\n#endif\n\n#elif defined(_MSC_VER) && defined(_M_X64)\n#if WYHASH_CONDOM2\n        uint64_t a, b;\n        a = _umul128(A, B, &b);\n        A ^= a; B ^= b;\n#else\n        A = _umul128(A, B, &B);\n#endif\n#else\n        uint64_t ha = A >> 32, hb = B >> 32, la = (uint32_t)A, lb = (uint32_t)B, hi, lo;\n        uint64_t rh = ha * hb, rm0 = ha * lb, rm1 = hb * la, rl = la * lb, t = rl + (rm0 << 32), c = t < rl;\n        lo = t + (rm1 << 32); c += lo < t; hi = rh + (rm0 >> 32) + (rm1 >> 32) + c;\n#if WYHASH_CONDOM2\n        A ^= lo; B ^= hi;\n#else\n        A = lo; B = hi;\n#endif\n#endif\n        return A ^ B;\n    }\n\n    //multiply and xor mix function, aka MUM\n    static inline uint64_t wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v; }\n    static inline uint64_t wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v; }\n    static inline uint64_t wyr3(const uint8_t *p, size_t k) {\n        return (((uint64_t)p[0]) << 16) | (((uint64_t)p[k >> 1]) << 8) | p[k - 1];\n    }\n\n    inline static const uint64_t secret[4] = {\n        0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull,\n        0x4b33a62ed433d4a3ull, 0x4d5a2da51de1aa47ull};\npublic:\n    //wyhash main function https://github.com/wangyi-fudan/wyhash\n    static uint64_t wyhashstr(const char *key, const size_t len)\n    {\n        uint64_t a = 0, b = 0, seed = secret[0];\n        const uint8_t *p = (const uint8_t*)key;\n        if (EMH_LIKELY(len <= 16)) {\n            if (EMH_LIKELY(len >= 4)) {\n                const auto half = (len >> 3) << 2;\n                a = (wyr4(p) << 32U) | wyr4(p + half); p += len - 4;\n                b = (wyr4(p) << 32U) | wyr4(p - half);\n            } else if (len) {\n                a = wyr3(p, len);\n            }\n        } else {\n            size_t i = len;\n            if (EMH_UNLIKELY(i > 48)) {\n                uint64_t see1 = seed, see2 = seed;\n                do {\n                    seed = wymix(wyr8(p +  0) ^ secret[1], wyr8(p +  8) ^ seed);\n                    see1 = wymix(wyr8(p + 16) ^ secret[2], wyr8(p + 24) ^ see1);\n                    see2 = wymix(wyr8(p + 32) ^ secret[3], wyr8(p + 40) ^ see2);\n                    p += 48; i -= 48;\n                } while (EMH_LIKELY(i > 48));\n                seed ^= see1 ^ see2;\n            }\n            while (i > 16) {\n                seed = wymix(wyr8(p) ^ secret[1], wyr8(p + 8) ^ seed);\n                i -= 16; p += 16;\n            }\n            a = wyr8(p + i - 16);\n            b = wyr8(p + i - 8);\n        }\n\n        return wymix(secret[1] ^ len, wymix(a ^ secret[1], b ^ seed));\n    }\n#endif\n\nprivate:\n    template<typename UType, typename std::enable_if<std::is_integral<UType>::value, uint32_t>::type = 0>\n        inline uint64_t hash_key(const UType key) const\n        {\n#if EMH_INT_HASH\n            return hash64(key);\n#elif EMH_IDENTITY_HASH\n            return key + (key >> 24);\n#else\n            return _hasher(key);\n#endif\n        }\n\n    template<typename UType, typename std::enable_if<std::is_same<UType, std::string>::value, uint32_t>::type = 0>\n        inline uint64_t hash_key(const UType& key) const\n        {\n#if EMH_WYHASH_HASH\n            return wyhashstr(key.data(), key.size());\n#else\n            return _hasher(key);\n#endif\n        }\n\n    template<typename UType, typename std::enable_if<!std::is_integral<UType>::value && !std::is_same<UType, std::string>::value, uint32_t>::type = 0>\n        inline uint64_t hash_key(const UType& key) const\n        {\n            return _hasher(key);\n        }\n\nprivate:\n    Index*    _index;\n    value_type*_pairs;\n\n    HashT     _hasher;\n    EqT       _eq;\n    uint32_t  _mlf;\n    size_type _mask;\n    size_type _num_buckets;\n    size_type _num_filled;\n    size_type _last;\n#if EMH_HIGH_LOAD\n    size_type _ehead;\n#endif\n    size_type _etail;\n};\n} // namespace emhash\n\n"
  },
  {
    "path": "src/third_party/rapidhash/README.ninja",
    "content": "Description: Very fast, high quality, platform-independent hashing algorithm.\nVersion: commit 4a6b2570e868536be84800353efd92c699f37d2c\nURL: https://github.com/Nicoshev/rapidhash\nCopyright: Copyright (C) 2024 Nicolas De Carli, Based on 'wyhash', by Wang Yi <godspeed_china@yeah.net>\nSPDX-License-Identifier: BSD-2-Clause\nLocal changes:\n - Changed to UNIX line endings\n"
  },
  {
    "path": "src/third_party/rapidhash/rapidhash.h",
    "content": "/*\n * rapidhash - Very fast, high quality, platform-independent hashing algorithm.\n * Copyright (C) 2024 Nicolas De Carli\n *\n * Based on 'wyhash', by Wang Yi <godspeed_china@yeah.net>\n *\n * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above\n *      copyright notice, this list of conditions and the following disclaimer\n *      in the documentation and/or other materials provided with the\n *      distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * You can contact the author at:\n *   - rapidhash source repository: https://github.com/Nicoshev/rapidhash\n */\n\n/*\n *  Includes.\n */\n#include <stdint.h>\n#include <string.h>\n#if defined(_MSC_VER)\n  #include <intrin.h>\n  #if defined(_M_X64) && !defined(_M_ARM64EC)\n    #pragma intrinsic(_umul128)\n  #endif\n#endif\n\n/*\n *  C++ macros.\n *\n *  RAPIDHASH_INLINE can be overridden to be stronger than a hint, i.e. by adding __attribute__((always_inline)).\n */\n#ifdef __cplusplus\n  #define RAPIDHASH_NOEXCEPT noexcept\n  #define RAPIDHASH_CONSTEXPR constexpr\n  #ifndef RAPIDHASH_INLINE\n    #define RAPIDHASH_INLINE inline\n  #endif\n#else\n  #define RAPIDHASH_NOEXCEPT\n  #define RAPIDHASH_CONSTEXPR static const\n  #ifndef RAPIDHASH_INLINE\n    #define RAPIDHASH_INLINE static inline\n  #endif\n#endif\n\n/*\n *  Protection macro, alters behaviour of rapid_mum multiplication function.\n *\n *  RAPIDHASH_FAST: Normal behavior, max speed.\n *  RAPIDHASH_PROTECTED: Extra protection against entropy loss.\n */\n#ifndef RAPIDHASH_PROTECTED\n  #define RAPIDHASH_FAST\n#elif defined(RAPIDHASH_FAST)\n  #error \"cannot define RAPIDHASH_PROTECTED and RAPIDHASH_FAST simultaneously.\"\n#endif\n\n/*\n *  Unrolling macros, changes code definition for main hash function.\n *\n *  RAPIDHASH_COMPACT: Legacy variant, each loop process 48 bytes.\n *  RAPIDHASH_UNROLLED: Unrolled variant, each loop process 96 bytes.\n *\n *  Most modern CPUs should benefit from having RAPIDHASH_UNROLLED.\n *\n *  These macros do not alter the output hash.\n */\n#ifndef RAPIDHASH_COMPACT\n  #define RAPIDHASH_UNROLLED\n#elif defined(RAPIDHASH_UNROLLED)\n  #error \"cannot define RAPIDHASH_COMPACT and RAPIDHASH_UNROLLED simultaneously.\"\n#endif\n\n/*\n *  Likely and unlikely macros.\n */\n#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)\n  #define _likely_(x)  __builtin_expect(x,1)\n  #define _unlikely_(x)  __builtin_expect(x,0)\n#else\n  #define _likely_(x) (x)\n  #define _unlikely_(x) (x)\n#endif\n\n/*\n *  Endianness macros.\n */\n#ifndef RAPIDHASH_LITTLE_ENDIAN\n  #if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n    #define RAPIDHASH_LITTLE_ENDIAN\n  #elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)\n    #define RAPIDHASH_BIG_ENDIAN\n  #else\n    #warning \"could not determine endianness! Falling back to little endian.\"\n    #define RAPIDHASH_LITTLE_ENDIAN\n  #endif\n#endif\n\n/*\n *  Default seed.\n */\n#define RAPID_SEED (0xbdd89aa982704029ull)\n\n/*\n *  Default secret parameters.\n */\nRAPIDHASH_CONSTEXPR uint64_t rapid_secret[3] = {0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, 0x4b33a62ed433d4a3ull};\n\n/*\n *  64*64 -> 128bit multiply function.\n *\n *  @param A  Address of 64-bit number.\n *  @param B  Address of 64-bit number.\n *\n *  Calculates 128-bit C = *A * *B.\n *\n *  When RAPIDHASH_FAST is defined:\n *  Overwrites A contents with C's low 64 bits.\n *  Overwrites B contents with C's high 64 bits.\n *\n *  When RAPIDHASH_PROTECTED is defined:\n *  Xors and overwrites A contents with C's low 64 bits.\n *  Xors and overwrites B contents with C's high 64 bits.\n */\nRAPIDHASH_INLINE void rapid_mum(uint64_t *A, uint64_t *B) RAPIDHASH_NOEXCEPT {\n#if defined(__SIZEOF_INT128__)\n  __uint128_t r=*A; r*=*B;\n  #ifdef RAPIDHASH_PROTECTED\n  *A^=(uint64_t)r; *B^=(uint64_t)(r>>64);\n  #else\n  *A=(uint64_t)r; *B=(uint64_t)(r>>64);\n  #endif\n#elif defined(_MSC_VER) && (defined(_WIN64) || defined(_M_HYBRID_CHPE_ARM64))\n  #if defined(_M_X64)\n    #ifdef RAPIDHASH_PROTECTED\n    uint64_t a, b;\n    a=_umul128(*A,*B,&b);\n    *A^=a;  *B^=b;\n    #else\n    *A=_umul128(*A,*B,B);\n    #endif\n  #else\n    #ifdef RAPIDHASH_PROTECTED\n    uint64_t a, b;\n    b = __umulh(*A, *B);\n    a = *A * *B;\n    *A^=a;  *B^=b;\n    #else\n    uint64_t c = __umulh(*A, *B);\n    *A = *A * *B;\n    *B = c;\n    #endif\n  #endif\n#else\n  uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;\n  uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;\n  lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c;\n  #ifdef RAPIDHASH_PROTECTED\n  *A^=lo;  *B^=hi;\n  #else\n  *A=lo;  *B=hi;\n  #endif\n#endif\n}\n\n/*\n *  Multiply and xor mix function.\n *\n *  @param A  64-bit number.\n *  @param B  64-bit number.\n *\n *  Calculates 128-bit C = A * B.\n *  Returns 64-bit xor between high and low 64 bits of C.\n */\nRAPIDHASH_INLINE uint64_t rapid_mix(uint64_t A, uint64_t B) RAPIDHASH_NOEXCEPT { rapid_mum(&A,&B); return A^B; }\n\n/*\n *  Read functions.\n */\n#ifdef RAPIDHASH_LITTLE_ENDIAN\nRAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return v;}\nRAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return v;}\n#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)\nRAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return __builtin_bswap64(v);}\nRAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return __builtin_bswap32(v);}\n#elif defined(_MSC_VER)\nRAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return _byteswap_uint64(v);}\nRAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return _byteswap_ulong(v);}\n#else\nRAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT {\n  uint64_t v; memcpy(&v, p, 8);\n  return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >>  8) & 0xff000000)| ((v <<  8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000));\n}\nRAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT {\n  uint32_t v; memcpy(&v, p, 4);\n  return (((v >> 24) & 0xff)| ((v >>  8) & 0xff00)| ((v <<  8) & 0xff0000)| ((v << 24) & 0xff000000));\n}\n#endif\n\n/*\n *  Reads and combines 3 bytes of input.\n *\n *  @param p  Buffer to read from.\n *  @param k  Length of @p, in bytes.\n *\n *  Always reads and combines 3 bytes from memory.\n *  Guarantees to read each buffer position at least once.\n *\n *  Returns a 64-bit value containing all three bytes read.\n */\nRAPIDHASH_INLINE uint64_t rapid_readSmall(const uint8_t *p, size_t k) RAPIDHASH_NOEXCEPT { return (((uint64_t)p[0])<<56)|(((uint64_t)p[k>>1])<<32)|p[k-1];}\n\n/*\n *  rapidhash main function.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *  @param seed    64-bit seed used to alter the hash result predictably.\n *  @param secret  Triplet of 64-bit secrets used to alter hash result predictably.\n *\n *  Returns a 64-bit hash.\n */\nRAPIDHASH_INLINE uint64_t rapidhash_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT {\n  const uint8_t *p=(const uint8_t *)key; seed^=rapid_mix(seed^secret[0],secret[1])^len;  uint64_t  a,  b;\n  if(_likely_(len<=16)){\n    if(_likely_(len>=4)){\n      const uint8_t * plast = p + len - 4;\n      a = (rapid_read32(p) << 32) | rapid_read32(plast);\n      const uint64_t delta = ((len&24)>>(len>>3));\n      b = ((rapid_read32(p + delta) << 32) | rapid_read32(plast - delta)); }\n    else if(_likely_(len>0)){ a=rapid_readSmall(p,len); b=0;}\n    else a=b=0;\n  }\n  else{\n    size_t i=len;\n    if(_unlikely_(i>48)){\n      uint64_t see1=seed, see2=seed;\n#ifdef RAPIDHASH_UNROLLED\n      while(_likely_(i>=96)){\n        seed=rapid_mix(rapid_read64(p)^secret[0],rapid_read64(p+8)^seed);\n        see1=rapid_mix(rapid_read64(p+16)^secret[1],rapid_read64(p+24)^see1);\n        see2=rapid_mix(rapid_read64(p+32)^secret[2],rapid_read64(p+40)^see2);\n        seed=rapid_mix(rapid_read64(p+48)^secret[0],rapid_read64(p+56)^seed);\n        see1=rapid_mix(rapid_read64(p+64)^secret[1],rapid_read64(p+72)^see1);\n        see2=rapid_mix(rapid_read64(p+80)^secret[2],rapid_read64(p+88)^see2);\n        p+=96; i-=96;\n      }\n      if(_unlikely_(i>=48)){\n        seed=rapid_mix(rapid_read64(p)^secret[0],rapid_read64(p+8)^seed);\n        see1=rapid_mix(rapid_read64(p+16)^secret[1],rapid_read64(p+24)^see1);\n        see2=rapid_mix(rapid_read64(p+32)^secret[2],rapid_read64(p+40)^see2);\n        p+=48; i-=48;\n      }\n#else\n      do {\n        seed=rapid_mix(rapid_read64(p)^secret[0],rapid_read64(p+8)^seed);\n        see1=rapid_mix(rapid_read64(p+16)^secret[1],rapid_read64(p+24)^see1);\n        see2=rapid_mix(rapid_read64(p+32)^secret[2],rapid_read64(p+40)^see2);\n        p+=48; i-=48;\n      } while (_likely_(i>=48));\n#endif\n      seed^=see1^see2;\n    }\n    if(i>16){\n      seed=rapid_mix(rapid_read64(p)^secret[2],rapid_read64(p+8)^seed^secret[1]);\n      if(i>32)\n        seed=rapid_mix(rapid_read64(p+16)^secret[2],rapid_read64(p+24)^seed);\n    }\n    a=rapid_read64(p+i-16);  b=rapid_read64(p+i-8);\n  }\n  a^=secret[1]; b^=seed;  rapid_mum(&a,&b);\n  return  rapid_mix(a^secret[0]^len,b^secret[1]);\n}\n\n/*\n *  rapidhash default seeded hash function.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *  @param seed    64-bit seed used to alter the hash result predictably.\n *\n *  Calls rapidhash_internal using provided parameters and default secrets.\n *\n *  Returns a 64-bit hash.\n */\nRAPIDHASH_INLINE uint64_t rapidhash_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT {\n  return rapidhash_internal(key, len, seed, rapid_secret);\n}\n\n/*\n *  rapidhash default hash function.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *\n *  Calls rapidhash_withSeed using provided parameters and the default seed.\n *\n *  Returns a 64-bit hash.\n */\nRAPIDHASH_INLINE uint64_t rapidhash(const void *key, size_t len) RAPIDHASH_NOEXCEPT {\n  return rapidhash_withSeed(key, len, RAPID_SEED);\n}\n"
  },
  {
    "path": "src/timestamp.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_TIMESTAMP_H_\n#define NINJA_TIMESTAMP_H_\n\n#ifdef _WIN32\n#include \"win32port.h\"\n#else\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n#include <inttypes.h>\n#endif\n\n// When considering file modification times we only care to compare\n// them against one another -- we never convert them to an absolute\n// real time.  On POSIX we use timespec (seconds&nanoseconds since epoch)\n// and on Windows we use a different value.  Both fit in an int64.\ntypedef int64_t TimeStamp;\n\n#endif  // NINJA_TIMESTAMP_H_\n"
  },
  {
    "path": "src/util.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util.h\"\n\n#ifdef __CYGWIN__\n#include <windows.h>\n#include <io.h>\n#elif defined( _WIN32)\n#include <windows.h>\n#include <io.h>\n#include <share.h>\n#include <direct.h>\n#endif\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#ifndef _WIN32\n#include <unistd.h>\n#include <sys/time.h>\n#endif\n\n#include <algorithm>\n#include <vector>\n\n#if defined(__APPLE__) || defined(__FreeBSD__)\n#include <sys/sysctl.h>\n#elif defined(__SVR4) && defined(__sun)\n#include <unistd.h>\n#include <sys/loadavg.h>\n#elif defined(_AIX) && !defined(__PASE__)\n#include <libperfstat.h>\n#elif defined(__linux__) || defined(__GLIBC__)\n#include <sys/sysinfo.h>\n#include <fstream>\n#include <map>\n#include \"string_piece_util.h\"\n#endif\n\n#if defined(__FreeBSD__)\n#include <sys/cpuset.h>\n#endif\n\n#include \"edit_distance.h\"\n\nusing namespace std;\n\nvoid Fatal(const char* msg, ...) {\n  va_list ap;\n  fprintf(stderr, \"ninja: fatal: \");\n  va_start(ap, msg);\n  vfprintf(stderr, msg, ap);\n  va_end(ap);\n  fprintf(stderr, \"\\n\");\n#ifdef _WIN32\n  // On Windows, some tools may inject extra threads.\n  // exit() may block on locks held by those threads, so forcibly exit.\n  fflush(stderr);\n  fflush(stdout);\n  ExitProcess(1);\n#else\n  exit(1);\n#endif\n}\n\nvoid Warning(const char* msg, va_list ap) {\n  fprintf(stderr, \"ninja: warning: \");\n  vfprintf(stderr, msg, ap);\n  fprintf(stderr, \"\\n\");\n}\n\nvoid Warning(const char* msg, ...) {\n  va_list ap;\n  va_start(ap, msg);\n  Warning(msg, ap);\n  va_end(ap);\n}\n\nvoid Error(const char* msg, va_list ap) {\n  fprintf(stderr, \"ninja: error: \");\n  vfprintf(stderr, msg, ap);\n  fprintf(stderr, \"\\n\");\n}\n\nvoid Error(const char* msg, ...) {\n  va_list ap;\n  va_start(ap, msg);\n  Error(msg, ap);\n  va_end(ap);\n}\n\nvoid Info(const char* msg, va_list ap) {\n  fprintf(stdout, \"ninja: \");\n  vfprintf(stdout, msg, ap);\n  fprintf(stdout, \"\\n\");\n}\n\nvoid Info(const char* msg, ...) {\n  va_list ap;\n  va_start(ap, msg);\n  Info(msg, ap);\n  va_end(ap);\n}\n\nvoid CanonicalizePath(string* path, uint64_t* slash_bits) {\n  size_t len = path->size();\n  char* str = 0;\n  if (len > 0)\n    str = &(*path)[0];\n  CanonicalizePath(str, &len, slash_bits);\n  path->resize(len);\n}\n\nstatic bool IsPathSeparator(char c) {\n#ifdef _WIN32\n  return c == '/' || c == '\\\\';\n#else\n  return c == '/';\n#endif\n}\n\nvoid CanonicalizePath(char* path, size_t* len, uint64_t* slash_bits) {\n  // WARNING: this function is performance-critical; please benchmark\n  // any changes you make to it.\n  if (*len == 0) {\n    return;\n  }\n\n  char* start = path;\n  char* dst = start;\n  char* dst_start = dst;\n  const char* src = start;\n  const char* end = start + *len;\n  const char* src_next;\n\n  // For absolute paths, skip the leading directory separator\n  // as this one should never be removed from the result.\n  if (IsPathSeparator(*src)) {\n#ifdef _WIN32\n    // Windows network path starts with //\n    if (src + 2 <= end && IsPathSeparator(src[1])) {\n      src += 2;\n      dst += 2;\n    } else {\n      ++src;\n      ++dst;\n    }\n#else\n    ++src;\n    ++dst;\n#endif\n    dst_start = dst;\n  } else {\n    // For relative paths, skip any leading ../ as these are quite common\n    // to reference source files in build plans, and doing this here makes\n    // the loop work below faster in general.\n    while (src + 3 <= end && src[0] == '.' && src[1] == '.' &&\n           IsPathSeparator(src[2])) {\n      src += 3;\n      dst += 3;\n    }\n  }\n\n  // Loop over all components of the paths _except_ the last one, in\n  // order to simplify the loop's code and make it faster.\n  int component_count = 0;\n  char* dst0 = dst;\n  for (; src < end; src = src_next) {\n#ifndef _WIN32\n    // Use memchr() for faster lookups thanks to optimized C library\n    // implementation. `hyperfine canon_perftest` shows a significant\n    // difference (e,g, 484ms vs 437ms).\n    const char* next_sep =\n        static_cast<const char*>(::memchr(src, '/', end - src));\n    if (!next_sep) {\n      // This is the last component, will be handled out of the loop.\n      break;\n    }\n#else\n    // Need to check for both '/' and '\\\\' so do not use memchr().\n    // Cannot use strpbrk() because end[0] can be \\0 or something else!\n    const char* next_sep = src;\n    while (next_sep != end && !IsPathSeparator(*next_sep))\n      ++next_sep;\n    if (next_sep == end) {\n      // This is the last component, will be handled out of the loop.\n      break;\n    }\n#endif\n    // Position for next loop iteration.\n    src_next = next_sep + 1;\n    // Length of the component, excluding trailing directory.\n    size_t component_len = next_sep - src;\n\n    if (component_len <= 2) {\n      if (component_len == 0) {\n        continue;  // Ignore empty component, e.g. 'foo//bar' -> 'foo/bar'.\n      }\n      if (src[0] == '.') {\n        if (component_len == 1) {\n          continue;  // Ignore '.' component, e.g. './foo' -> 'foo'.\n        } else if (src[1] == '.') {\n          // Process the '..' component if found. Back up if possible.\n          if (component_count > 0) {\n            // Move back to start of previous component.\n            --component_count;\n            while (--dst > dst0 && !IsPathSeparator(dst[-1])) {\n              // nothing to do here, decrement happens before condition check.\n            }\n          } else {\n            dst[0] = '.';\n            dst[1] = '.';\n            dst[2] = src[2];\n            dst += 3;\n          }\n          continue;\n        }\n      }\n    }\n    ++component_count;\n\n    // Copy or skip component, including trailing directory separator.\n    if (dst != src) {\n      ::memmove(dst, src, src_next - src);\n    }\n    dst += src_next - src;\n  }\n\n  // Handling the last component that does not have a trailing separator.\n  // The logic here is _slightly_ different since there is no trailing\n  // directory separator.\n  size_t component_len = end - src;\n  do {\n    if (component_len == 0)\n      break;  // Ignore empty component (e.g. 'foo//' -> 'foo/')\n    if (src[0] == '.') {\n      if (component_len == 1)\n        break;  // Ignore trailing '.' (e.g. 'foo/.' -> 'foo/')\n      if (component_len == 2 && src[1] == '.') {\n        // Handle '..'. Back up if possible.\n        if (component_count > 0) {\n          while (--dst > dst0 && !IsPathSeparator(dst[-1])) {\n            // nothing to do here, decrement happens before condition check.\n          }\n        } else {\n          dst[0] = '.';\n          dst[1] = '.';\n          dst += 2;\n          // No separator to add here.\n        }\n        break;\n      }\n    }\n    // Skip or copy last component, no trailing separator.\n    if (dst != src) {\n      ::memmove(dst, src, component_len);\n    }\n    dst += component_len;\n  } while (0);\n\n  // Remove trailing path separator if any, but keep the initial\n  // path separator(s) if there was one (or two on Windows).\n  if (dst > dst_start && IsPathSeparator(dst[-1]))\n    dst--;\n\n  if (dst == start) {\n    // Handle special cases like \"aa/..\" -> \".\"\n    *dst++ = '.';\n  }\n\n  *len = dst - start;  // dst points after the trailing char here.\n#ifdef _WIN32\n  uint64_t bits = 0;\n  uint64_t bits_mask = 1;\n\n  for (char* c = start; c < start + *len; ++c) {\n    switch (*c) {\n      case '\\\\':\n        bits |= bits_mask;\n        *c = '/';\n        NINJA_FALLTHROUGH;\n      case '/':\n        bits_mask <<= 1;\n    }\n  }\n\n  *slash_bits = bits;\n#else\n  *slash_bits = 0;\n#endif\n}\n\nstatic inline bool IsKnownShellSafeCharacter(char ch) {\n  if ('A' <= ch && ch <= 'Z') return true;\n  if ('a' <= ch && ch <= 'z') return true;\n  if ('0' <= ch && ch <= '9') return true;\n\n  switch (ch) {\n    case '_':\n    case '+':\n    case '-':\n    case '.':\n    case '/':\n      return true;\n    default:\n      return false;\n  }\n}\n\nstatic inline bool IsKnownWin32SafeCharacter(char ch) {\n  switch (ch) {\n    case ' ':\n    case '\"':\n      return false;\n    default:\n      return true;\n  }\n}\n\nstatic inline bool StringNeedsShellEscaping(const string& input) {\n  for (size_t i = 0; i < input.size(); ++i) {\n    if (!IsKnownShellSafeCharacter(input[i])) return true;\n  }\n  return false;\n}\n\nstatic inline bool StringNeedsWin32Escaping(const string& input) {\n  for (size_t i = 0; i < input.size(); ++i) {\n    if (!IsKnownWin32SafeCharacter(input[i])) return true;\n  }\n  return false;\n}\n\nvoid GetShellEscapedString(const string& input, string* result) {\n  assert(result);\n\n  if (!StringNeedsShellEscaping(input)) {\n    result->append(input);\n    return;\n  }\n\n  const char kQuote = '\\'';\n  const char kEscapeSequence[] = \"'\\\\'\";\n\n  result->push_back(kQuote);\n\n  string::const_iterator span_begin = input.begin();\n  for (string::const_iterator it = input.begin(), end = input.end(); it != end;\n       ++it) {\n    if (*it == kQuote) {\n      result->append(span_begin, it);\n      result->append(kEscapeSequence);\n      span_begin = it;\n    }\n  }\n  result->append(span_begin, input.end());\n  result->push_back(kQuote);\n}\n\n\nvoid GetWin32EscapedString(const string& input, string* result) {\n  assert(result);\n  if (!StringNeedsWin32Escaping(input)) {\n    result->append(input);\n    return;\n  }\n\n  const char kQuote = '\"';\n  const char kBackslash = '\\\\';\n\n  result->push_back(kQuote);\n  size_t consecutive_backslash_count = 0;\n  string::const_iterator span_begin = input.begin();\n  for (string::const_iterator it = input.begin(), end = input.end(); it != end;\n       ++it) {\n    switch (*it) {\n      case kBackslash:\n        ++consecutive_backslash_count;\n        break;\n      case kQuote:\n        result->append(span_begin, it);\n        result->append(consecutive_backslash_count + 1, kBackslash);\n        span_begin = it;\n        consecutive_backslash_count = 0;\n        break;\n      default:\n        consecutive_backslash_count = 0;\n        break;\n    }\n  }\n  result->append(span_begin, input.end());\n  result->append(consecutive_backslash_count, kBackslash);\n  result->push_back(kQuote);\n}\n\nint ReadFile(const string& path, string* contents, string* err) {\n#ifdef _WIN32\n  // This makes a ninja run on a set of 1500 manifest files about 4% faster\n  // than using the generic fopen code below.\n  err->clear();\n  HANDLE f = ::CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,\n                           OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);\n  if (f == INVALID_HANDLE_VALUE) {\n    err->assign(GetLastErrorString());\n    return -ENOENT;\n  }\n\n  for (;;) {\n    DWORD len;\n    char buf[64 << 10];\n    if (!::ReadFile(f, buf, sizeof(buf), &len, NULL)) {\n      err->assign(GetLastErrorString());\n      contents->clear();\n      ::CloseHandle(f);\n      return -EIO;\n    }\n    if (len == 0)\n      break;\n    contents->append(buf, len);\n  }\n  ::CloseHandle(f);\n  return 0;\n#else\n  FILE* f = fopen(path.c_str(), \"rb\");\n  if (!f) {\n    err->assign(strerror(errno));\n    return -errno;\n  }\n\n#ifdef __USE_LARGEFILE64\n  struct stat64 st;\n  if (fstat64(fileno(f), &st) < 0) {\n#else\n  struct stat st;\n  if (fstat(fileno(f), &st) < 0) {\n#endif\n    err->assign(strerror(errno));\n    fclose(f);\n    return -errno;\n  }\n\n  // +1 is for the resize in ManifestParser::Load\n  contents->reserve(st.st_size + 1);\n\n  char buf[64 << 10];\n  size_t len;\n  while (!feof(f) && (len = fread(buf, 1, sizeof(buf), f)) > 0) {\n    contents->append(buf, len);\n  }\n  if (ferror(f)) {\n    err->assign(strerror(errno));  // XXX errno?\n    contents->clear();\n    fclose(f);\n    return -errno;\n  }\n  fclose(f);\n  return 0;\n#endif\n}\n\nvoid SetCloseOnExec(int fd) {\n#ifndef _WIN32\n  int flags = fcntl(fd, F_GETFD);\n  if (flags < 0) {\n    perror(\"fcntl(F_GETFD)\");\n  } else {\n    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)\n      perror(\"fcntl(F_SETFD)\");\n  }\n#else\n  HANDLE hd = (HANDLE) _get_osfhandle(fd);\n  if (! SetHandleInformation(hd, HANDLE_FLAG_INHERIT, 0)) {\n    fprintf(stderr, \"SetHandleInformation(): %s\", GetLastErrorString().c_str());\n  }\n#endif  // ! _WIN32\n}\n\n\nconst char* SpellcheckStringV(const string& text,\n                              const vector<const char*>& words) {\n  const bool kAllowReplacements = true;\n  const int kMaxValidEditDistance = 3;\n\n  int min_distance = kMaxValidEditDistance + 1;\n  const char* result = NULL;\n  for (vector<const char*>::const_iterator i = words.begin();\n       i != words.end(); ++i) {\n    int distance = EditDistance(*i, text, kAllowReplacements,\n                                kMaxValidEditDistance);\n    if (distance < min_distance) {\n      min_distance = distance;\n      result = *i;\n    }\n  }\n  return result;\n}\n\nconst char* SpellcheckString(const char* text, ...) {\n  // Note: This takes a const char* instead of a string& because using\n  // va_start() with a reference parameter is undefined behavior.\n  va_list ap;\n  va_start(ap, text);\n  vector<const char*> words;\n  const char* word;\n  while ((word = va_arg(ap, const char*)))\n    words.push_back(word);\n  va_end(ap);\n  return SpellcheckStringV(text, words);\n}\n\n#ifdef _WIN32\nstring GetLastErrorString() {\n  DWORD err = GetLastError();\n\n  char* msg_buf;\n  FormatMessageA(\n        FORMAT_MESSAGE_ALLOCATE_BUFFER |\n        FORMAT_MESSAGE_FROM_SYSTEM |\n        FORMAT_MESSAGE_IGNORE_INSERTS,\n        NULL,\n        err,\n        MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),\n        (char*)&msg_buf,\n        0,\n        NULL);\n\n  if (msg_buf == nullptr) {\n    char fallback_msg[128] = {0};\n    snprintf(fallback_msg, sizeof(fallback_msg), \"GetLastError() = %lu\", err);\n    return fallback_msg;\n  }\n\n  string msg = msg_buf;\n  LocalFree(msg_buf);\n  return msg;\n}\n\nvoid Win32Fatal(const char* function, const char* hint) {\n  if (hint) {\n    Fatal(\"%s: %s (%s)\", function, GetLastErrorString().c_str(), hint);\n  } else {\n    Fatal(\"%s: %s\", function, GetLastErrorString().c_str());\n  }\n}\n#endif\n\nbool islatinalpha(int c) {\n  // isalpha() is locale-dependent.\n  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');\n}\n\nstring StripAnsiEscapeCodes(const string& in) {\n  string stripped;\n  stripped.reserve(in.size());\n\n  for (size_t i = 0; i < in.size(); ++i) {\n    if (in[i] != '\\33') {\n      // Not an escape code.\n      stripped.push_back(in[i]);\n      continue;\n    }\n\n    // Only strip CSIs for now.\n    if (i + 1 >= in.size()) break;\n    if (in[i + 1] != '[') continue;  // Not a CSI.\n    i += 2;\n\n    // Skip everything up to and including the next [a-zA-Z].\n    while (i < in.size() && !islatinalpha(in[i]))\n      ++i;\n  }\n  return stripped;\n}\n\n#if defined(__linux__) || defined(__GLIBC__)\nstd::pair<int64_t, bool> readCount(const std::string& path) {\n  std::ifstream file(path.c_str());\n  if (!file.is_open())\n    return std::make_pair(0, false);\n  int64_t n = 0;\n  file >> n;\n  if (file.good())\n    return std::make_pair(n, true);\n  return std::make_pair(0, false);\n}\n\nstruct MountPoint {\n  int mountId;\n  int parentId;\n  StringPiece deviceId;\n  StringPiece root;\n  StringPiece mountPoint;\n  vector<StringPiece> options;\n  vector<StringPiece> optionalFields;\n  StringPiece fsType;\n  StringPiece mountSource;\n  vector<StringPiece> superOptions;\n  bool parse(const string& line) {\n    vector<StringPiece> pieces = SplitStringPiece(line, ' ');\n    if (pieces.size() < 10)\n      return false;\n    size_t optionalStart = 0;\n    for (size_t i = 6; i < pieces.size(); i++) {\n      if (pieces[i] == \"-\") {\n        optionalStart = i + 1;\n        break;\n      }\n    }\n    if (optionalStart == 0)\n      return false;\n    if (optionalStart + 3 != pieces.size())\n      return false;\n    mountId = atoi(pieces[0].AsString().c_str());\n    parentId = atoi(pieces[1].AsString().c_str());\n    deviceId = pieces[2];\n    root = pieces[3];\n    mountPoint = pieces[4];\n    options = SplitStringPiece(pieces[5], ',');\n    optionalFields =\n        vector<StringPiece>(&pieces[6], &pieces[optionalStart - 1]);\n    fsType = pieces[optionalStart];\n    mountSource = pieces[optionalStart + 1];\n    superOptions = SplitStringPiece(pieces[optionalStart + 2], ',');\n    return true;\n  }\n  string translate(string& path) const {\n    // path must be sub dir of root\n    if (path.compare(0, root.len_, root.str_, root.len_) != 0) {\n      return string();\n    }\n    path.erase(0, root.len_);\n    if (path == \"..\" || (path.length() > 2 && path.compare(0, 3, \"../\") == 0)) {\n      return string();\n    }\n    return mountPoint.AsString() + \"/\" + path;\n  }\n};\n\nstruct CGroupSubSys {\n  int id;\n  string name;\n  vector<string> subsystems;\n  bool parse(string& line) {\n    size_t first = line.find(':');\n    if (first == string::npos)\n      return false;\n    line[first] = '\\0';\n    size_t second = line.find(':', first + 1);\n    if (second == string::npos)\n      return false;\n    line[second] = '\\0';\n    id = atoi(line.c_str());\n    name = line.substr(second + 1);\n    vector<StringPiece> pieces =\n        SplitStringPiece(StringPiece(line.c_str() + first + 1), ',');\n    for (size_t i = 0; i < pieces.size(); i++) {\n      subsystems.push_back(pieces[i].AsString());\n    }\n    return true;\n  }\n};\n\nmap<string, string> ParseMountInfo(map<string, CGroupSubSys>& subsystems) {\n  map<string, string> cgroups;\n  ifstream mountinfo(\"/proc/self/mountinfo\");\n  if (!mountinfo.is_open())\n    return cgroups;\n  while (!mountinfo.eof()) {\n    string line;\n    getline(mountinfo, line);\n    MountPoint mp;\n    if (!mp.parse(line))\n      continue;\n    if (mp.fsType == \"cgroup\") {\n      for (size_t i = 0; i < mp.superOptions.size(); i++) {\n        std::string opt = mp.superOptions[i].AsString();\n        auto subsys = subsystems.find(opt);\n        if (subsys == subsystems.end()) {\n          continue;\n        }\n        std::string newPath = mp.translate(subsys->second.name);\n        if (!newPath.empty()) {\n          cgroups.emplace(opt, newPath);\n        }\n      }\n    } else if (mp.fsType == \"cgroup2\") {\n      // Find cgroup2 entry in format \"0::/path/to/cgroup\"\n      auto subsys = std::find_if(subsystems.begin(), subsystems.end(),\n                                 [](const auto& sys) {\n                                   return sys.first == \"\" && sys.second.id == 0;\n                                 });\n      if (subsys == subsystems.end()) {\n        continue;\n      }\n      std::string path = mp.mountPoint.AsString();\n      if (subsys->second.name != \"/\") {\n        // Append the relative path for the cgroup to the mount point\n        path.append(subsys->second.name);\n      }\n      cgroups.emplace(\"cgroup2\", path);\n    }\n  }\n  return cgroups;\n}\n\nmap<string, CGroupSubSys> ParseSelfCGroup() {\n  map<string, CGroupSubSys> cgroups;\n  ifstream cgroup(\"/proc/self/cgroup\");\n  if (!cgroup.is_open())\n    return cgroups;\n  string line;\n  while (!cgroup.eof()) {\n    getline(cgroup, line);\n    CGroupSubSys subsys;\n    if (!subsys.parse(line))\n      continue;\n    for (size_t i = 0; i < subsys.subsystems.size(); i++) {\n      cgroups.insert(make_pair(subsys.subsystems[i], subsys));\n    }\n  }\n  return cgroups;\n}\n\nint ParseCgroupV1(std::string& path) {\n  std::pair<int64_t, bool> quota = readCount(path + \"/cpu.cfs_quota_us\");\n  if (!quota.second || quota.first == -1)\n    return -1;\n  std::pair<int64_t, bool> period = readCount(path + \"/cpu.cfs_period_us\");\n  if (!period.second)\n    return -1;\n  if (period.first == 0)\n    return -1;\n  return quota.first / period.first;\n}\n\nint ParseCgroupV2(std::string& path) {\n  // Read CPU quota from cgroup v2\n  std::ifstream cpu_max(path + \"/cpu.max\");\n  if (!cpu_max.is_open()) {\n    return -1;\n  }\n  std::string max_line;\n  if (!std::getline(cpu_max, max_line) || max_line.empty()) {\n    return -1;\n  }\n  // Format is \"quota period\" or \"max period\"\n  size_t space_pos = max_line.find(' ');\n  if (space_pos == string::npos) {\n    return -1;\n  }\n  std::string quota_str = max_line.substr(0, space_pos);\n  std::string period_str = max_line.substr(space_pos + 1);\n  if (quota_str == \"max\") {\n    return -1;  // No CPU limit set\n  }\n  // Convert quota string to integer\n  char* quota_end = nullptr;\n  errno = 0;\n  int64_t quota = strtoll(quota_str.c_str(), &quota_end, 10);\n  // Check for conversion errors\n  if (errno == ERANGE || quota_end == quota_str.c_str() || *quota_end != '\\0' ||\n      quota <= 0) {\n    return -1;\n  }\n  // Convert period string to integer\n  char* period_end = nullptr;\n  errno = 0;\n  int64_t period = strtoll(period_str.c_str(), &period_end, 10);\n  // Check for conversion errors\n  if (errno == ERANGE || period_end == period_str.c_str() ||\n      *period_end != '\\0' || period <= 0) {\n    return -1;\n  }\n  return quota / period;\n}\n\nint ParseCPUFromCGroup() {\n  auto subsystems = ParseSelfCGroup();\n  auto cgroups = ParseMountInfo(subsystems);\n\n  // Prefer cgroup v2 if both v1 and v2 should be present\n  const auto cgroup2 = cgroups.find(\"cgroup2\");\n  if (cgroup2 != cgroups.end()) {\n    return ParseCgroupV2(cgroup2->second);\n  }\n\n  const auto cpu = cgroups.find(\"cpu\");\n  if (cpu != cgroups.end()) {\n    return ParseCgroupV1(cpu->second);\n  }\n  return -1;\n}\n#endif\n\nint GetProcessorCount() {\n#ifdef _WIN32\n  DWORD cpuCount = 0;\n#ifndef _WIN64\n  // Need to use GetLogicalProcessorInformationEx to get real core count on\n  // machines with >64 cores. See https://stackoverflow.com/a/31209344/21475\n  DWORD len = 0;\n  if (!GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, &len)\n        && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {\n    std::vector<char> buf(len);\n    int cores = 0;\n    if (GetLogicalProcessorInformationEx(RelationProcessorCore,\n          reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(\n            buf.data()), &len)) {\n      for (DWORD i = 0; i < len; ) {\n        auto info = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(\n            buf.data() + i);\n        if (info->Relationship == RelationProcessorCore &&\n            info->Processor.GroupCount == 1) {\n          for (KAFFINITY core_mask = info->Processor.GroupMask[0].Mask;\n               core_mask; core_mask >>= 1) {\n            cores += (core_mask & 1);\n          }\n        }\n        i += info->Size;\n      }\n      if (cores != 0) {\n        cpuCount = cores;\n      }\n    }\n  }\n#endif\n  if (cpuCount == 0) {\n    cpuCount = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);\n  }\n  JOBOBJECT_CPU_RATE_CONTROL_INFORMATION info;\n  // reference:\n  // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_cpu_rate_control_information\n  if (QueryInformationJobObject(NULL, JobObjectCpuRateControlInformation, &info,\n                                sizeof(info), NULL)) {\n    if (info.ControlFlags & (JOB_OBJECT_CPU_RATE_CONTROL_ENABLE |\n                             JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP)) {\n      return cpuCount * info.CpuRate / 10000;\n    }\n  }\n  return cpuCount;\n#else\n  int cgroupCount = -1;\n  int schedCount = -1;\n#if defined(__linux__) || defined(__GLIBC__)\n  cgroupCount = ParseCPUFromCGroup();\n#endif\n  // The number of exposed processors might not represent the actual number of\n  // processors threads can run on. This happens when a CPU set limitation is\n  // active, see https://github.com/ninja-build/ninja/issues/1278\n#if defined(__FreeBSD__)\n  cpuset_t mask;\n  CPU_ZERO(&mask);\n  if (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(mask),\n    &mask) == 0) {\n    return CPU_COUNT(&mask);\n  }\n#elif defined(CPU_COUNT)\n  cpu_set_t set;\n  if (sched_getaffinity(getpid(), sizeof(set), &set) == 0) {\n    schedCount = CPU_COUNT(&set);\n  }\n#endif\n  if (cgroupCount >= 0 && schedCount >= 0) return std::min(cgroupCount, schedCount);\n  if (cgroupCount < 0 && schedCount < 0)\n    return static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));\n  return std::max(cgroupCount, schedCount);\n#endif\n}\n\n#if defined(_WIN32) || defined(__CYGWIN__)\nstatic double CalculateProcessorLoad(uint64_t idle_ticks, uint64_t total_ticks)\n{\n  static uint64_t previous_idle_ticks = 0;\n  static uint64_t previous_total_ticks = 0;\n  static double previous_load = -0.0;\n\n  uint64_t idle_ticks_since_last_time = idle_ticks - previous_idle_ticks;\n  uint64_t total_ticks_since_last_time = total_ticks - previous_total_ticks;\n\n  bool first_call = (previous_total_ticks == 0);\n  bool ticks_not_updated_since_last_call = (total_ticks_since_last_time == 0);\n\n  double load;\n  if (first_call || ticks_not_updated_since_last_call) {\n    load = previous_load;\n  } else {\n    // Calculate load.\n    double idle_to_total_ratio =\n        ((double)idle_ticks_since_last_time) / total_ticks_since_last_time;\n    double load_since_last_call = 1.0 - idle_to_total_ratio;\n\n    // Filter/smooth result when possible.\n    if(previous_load > 0) {\n      load = 0.9 * previous_load + 0.1 * load_since_last_call;\n    } else {\n      load = load_since_last_call;\n    }\n  }\n\n  previous_load = load;\n  previous_total_ticks = total_ticks;\n  previous_idle_ticks = idle_ticks;\n\n  return load;\n}\n\nstatic uint64_t FileTimeToTickCount(const FILETIME & ft)\n{\n  uint64_t high = (((uint64_t)(ft.dwHighDateTime)) << 32);\n  uint64_t low  = ft.dwLowDateTime;\n  return (high | low);\n}\n\ndouble GetLoadAverage() {\n  FILETIME idle_time, kernel_time, user_time;\n  BOOL get_system_time_succeeded =\n      GetSystemTimes(&idle_time, &kernel_time, &user_time);\n\n  double posix_compatible_load;\n  if (get_system_time_succeeded) {\n    uint64_t idle_ticks = FileTimeToTickCount(idle_time);\n\n    // kernel_time from GetSystemTimes already includes idle_time.\n    uint64_t total_ticks =\n        FileTimeToTickCount(kernel_time) + FileTimeToTickCount(user_time);\n\n    double processor_load = CalculateProcessorLoad(idle_ticks, total_ticks);\n    posix_compatible_load = processor_load * GetProcessorCount();\n\n  } else {\n    posix_compatible_load = -0.0;\n  }\n\n  return posix_compatible_load;\n}\n#elif defined(__PASE__)\ndouble GetLoadAverage() {\n  return -0.0f;\n}\n#elif defined(_AIX)\ndouble GetLoadAverage() {\n  perfstat_cpu_total_t cpu_stats;\n  if (perfstat_cpu_total(NULL, &cpu_stats, sizeof(cpu_stats), 1) < 0) {\n    return -0.0f;\n  }\n\n  // Calculation taken from comment in libperfstats.h\n  return double(cpu_stats.loadavg[0]) / double(1 << SBITS);\n}\n#elif defined(__UCLIBC__) || (defined(__BIONIC__) && __ANDROID_API__ < 29)\ndouble GetLoadAverage() {\n  struct sysinfo si;\n  if (sysinfo(&si) != 0)\n    return -0.0f;\n  return 1.0 / (1 << SI_LOAD_SHIFT) * si.loads[0];\n}\n#elif defined(__HAIKU__)\ndouble GetLoadAverage() {\n    return -0.0f;\n}\n#else\ndouble GetLoadAverage() {\n  double loadavg[3] = { 0.0f, 0.0f, 0.0f };\n  if (getloadavg(loadavg, 3) < 0) {\n    // Maybe we should return an error here or the availability of\n    // getloadavg(3) should be checked when ninja is configured.\n    return -0.0f;\n  }\n  return loadavg[0];\n}\n#endif // _WIN32\n\nstd::string GetWorkingDirectory() {\n  std::string ret;\n  char* success = NULL;\n  do {\n    ret.resize(ret.size() + 1024);\n    errno = 0;\n    success = getcwd(&ret[0], ret.size());\n  } while (!success && errno == ERANGE);\n  if (!success) {\n    Fatal(\"cannot determine working directory: %s\", strerror(errno));\n  }\n  ret.resize(strlen(&ret[0]));\n  return ret;\n}\n\nbool Truncate(const string& path, size_t size, string* err) {\n#ifdef _WIN32\n  int fh = _sopen(path.c_str(), _O_RDWR | _O_CREAT, _SH_DENYNO,\n                  _S_IREAD | _S_IWRITE);\n  int success = _chsize(fh, size);\n  _close(fh);\n#else\n  int success = truncate(path.c_str(), size);\n#endif\n  // Both truncate() and _chsize() return 0 on success and set errno and return\n  // -1 on failure.\n  if (success < 0) {\n    *err = strerror(errno);\n    return false;\n  }\n  return true;\n}\n\nbool ReplaceContent(const string& file_dst, const string& new_content,\n                    string* err) {\n#ifndef _WIN32\n  struct stat old_file;\n  bool found_uid_gid = true;\n\n  if (stat(file_dst.c_str(), &old_file) < 0) {\n    found_uid_gid = false;\n  }\n#endif\n\n  if (platformAwareUnlink(file_dst.c_str()) < 0) {\n    *err = strerror(errno);\n    return false;\n  }\n\n  if (rename(new_content.c_str(), file_dst.c_str()) < 0) {\n    *err = strerror(errno);\n    return false;\n  }\n\n#ifndef _WIN32\n  // apply uid and gid again so we always stay as the uid and gid of the first\n  // ninja invocation that created the file at file_dst\n  if (found_uid_gid) {\n    if (chown(file_dst.c_str(), old_file.st_uid, old_file.st_gid)) {\n      Warning(\"Reapplying previous uid and gid failed with: %s uid: %d gid: %d\",\n              strerror(errno), old_file.st_uid, old_file.st_gid);\n    }\n  }\n#endif\n\n  return true;\n}\n\nint platformAwareUnlink(const char* filename) {\n\t#ifdef _WIN32\n\t\treturn _unlink(filename);\n\t#else\n\t\treturn unlink(filename);\n\t#endif\n}\n"
  },
  {
    "path": "src/util.h",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_UTIL_H_\n#define NINJA_UTIL_H_\n\n#ifdef _WIN32\n#include \"win32port.h\"\n#else\n#include <stdint.h>\n#endif\n\n#include <stdarg.h>\n\n#include <string>\n#include <vector>\n\n#if !defined(__has_cpp_attribute)\n#  define __has_cpp_attribute(x)  0\n#endif\n\n#if __has_cpp_attribute(noreturn)\n#  define NORETURN [[noreturn]]\n#else\n#  define NORETURN  // nothing for old compilers\n#endif\n\n/// Log a fatal message and exit.\nNORETURN void Fatal(const char* msg, ...);\n\n// Have a generic fall-through for different versions of C/C++.\n#if __has_cpp_attribute(fallthrough)\n#  define NINJA_FALLTHROUGH [[fallthrough]]\n#elif defined(__clang__)\n#  define NINJA_FALLTHROUGH [[clang::fallthrough]]\n#else\n#  define NINJA_FALLTHROUGH // nothing\n#endif\n\n/// Log a warning message.\nvoid Warning(const char* msg, ...);\nvoid Warning(const char* msg, va_list ap);\n\n/// Log an error message.\nvoid Error(const char* msg, ...);\nvoid Error(const char* msg, va_list ap);\n\n/// Log an informational message.\nvoid Info(const char* msg, ...);\nvoid Info(const char* msg, va_list ap);\n\n/// Canonicalize a path like \"foo/../bar.h\" into just \"bar.h\".\n/// |slash_bits| has bits set starting from lowest for a backslash that was\n/// normalized to a forward slash. (only used on Windows)\nvoid CanonicalizePath(std::string* path, uint64_t* slash_bits);\nvoid CanonicalizePath(char* path, size_t* len, uint64_t* slash_bits);\n\n/// Appends |input| to |*result|, escaping according to the whims of either\n/// Bash, or Win32's CommandLineToArgvW().\n/// Appends the string directly to |result| without modification if we can\n/// determine that it contains no problematic characters.\nvoid GetShellEscapedString(const std::string& input, std::string* result);\nvoid GetWin32EscapedString(const std::string& input, std::string* result);\n\n/// Read a file to a string (in text mode: with CRLF conversion\n/// on Windows).\n/// Returns -errno and fills in \\a err on error.\nint ReadFile(const std::string& path, std::string* contents, std::string* err);\n\n/// Mark a file descriptor to not be inherited on exec()s.\nvoid SetCloseOnExec(int fd);\n\n/// Given a misspelled string and a list of correct spellings, returns\n/// the closest match or NULL if there is no close enough match.\nconst char* SpellcheckStringV(const std::string& text,\n                              const std::vector<const char*>& words);\n\n/// Like SpellcheckStringV, but takes a NULL-terminated list.\nconst char* SpellcheckString(const char* text, ...);\n\nbool islatinalpha(int c);\n\n/// Removes all Ansi escape codes (http://www.termsys.demon.co.uk/vtansi.htm).\nstd::string StripAnsiEscapeCodes(const std::string& in);\n\n/// @return the number of processors on the machine.  Useful for an initial\n/// guess for how many jobs to run in parallel.  @return 0 on error.\nint GetProcessorCount();\n\n/// @return the load average of the machine. A negative value is returned\n/// on error.\ndouble GetLoadAverage();\n\n/// a wrapper for getcwd()\nstd::string GetWorkingDirectory();\n\n/// Truncates a file to the given size.\nbool Truncate(const std::string& path, size_t size, std::string* err);\n\n#ifdef _MSC_VER\n#define snprintf _snprintf\n#define fileno _fileno\n#define chdir _chdir\n#define strtoull _strtoui64\n#define getcwd _getcwd\n#define PATH_MAX _MAX_PATH\n#endif\n\n\n/// ReplaceContent the content of \\a file_dst with the content of \\a\n/// new_content. On platforms other than Windows, the file pointed to by \\a\n/// file_dst will keep the same uid and gid, if possible. Reason for this is\n/// that permissions on Windows are more complex than on Linux, fetching\n/// permissions on Windows would involve an non-trivial amount of code to fetch\n/// users and have fallback paths\nbool ReplaceContent(const std::string& file_dst, const std::string& new_content,\n                    std::string* err);\n\n#ifdef _WIN32\n/// Convert the value returned by GetLastError() into a string.\nstd::string GetLastErrorString();\n\n/// Calls Fatal() with a function name and GetLastErrorString.\nNORETURN void Win32Fatal(const char* function, const char* hint = NULL);\n\n/// Naive implementation of C++ 20 std::bit_cast(), used to fix Clang and GCC\n/// [-Wcast-function-type] warning on casting result of GetProcAddress().\ntemplate <class To, class From>\ninline To FunctionCast(From from) {\n\tstatic_assert(sizeof(To) == sizeof(From), \"\");\n\tTo result;\n\tmemcpy(&result, &from, sizeof(To));\n\treturn result;\n}\n#endif\n\nint platformAwareUnlink(const char* filename);\n\n#endif  // NINJA_UTIL_H_\n"
  },
  {
    "path": "src/util_test.cc",
    "content": "// Copyright 2011 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util.h\"\n\n#include \"test.h\"\n\nusing namespace std;\n\nnamespace {\n\nvoid CanonicalizePath(string* path) {\n  uint64_t unused;\n  ::CanonicalizePath(path, &unused);\n}\n\n}  // namespace\n\nTEST(CanonicalizePath, PathSamples) {\n  string path;\n\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"\", path);\n\n  path = \"foo.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo.h\", path);\n\n  path = \"./foo.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo.h\", path);\n\n  path = \"./foo/./bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo/bar.h\", path);\n\n  path = \"./x/foo/../bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"x/bar.h\", path);\n\n  path = \"./x/foo/../../bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"bar.h\", path);\n\n  path = \"foo//bar\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo/bar\", path);\n\n  path = \"foo//.//..///bar\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"bar\", path);\n\n  path = \"./x/../foo/../../bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../bar.h\", path);\n\n  path = \"foo/./.\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo\", path);\n\n  path = \"foo/bar/..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo\", path);\n\n  path = \"foo/.hidden_bar\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo/.hidden_bar\", path);\n\n  path = \"/foo\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/foo\", path);\n\n  path = \"//foo\";\n  CanonicalizePath(&path);\n#ifdef _WIN32\n  EXPECT_EQ(\"//foo\", path);\n#else\n  EXPECT_EQ(\"/foo\", path);\n#endif\n\n  path = \"..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"..\", path);\n\n  path = \"../\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"..\", path);\n\n  path = \"../foo\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../foo\", path);\n\n  path = \"../foo/\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../foo\", path);\n\n  path = \"../..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../..\", path);\n\n  path = \"../../\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../..\", path);\n\n  path = \"./../\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"..\", path);\n\n  path = \"/..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/..\", path);\n\n  path = \"/../\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/..\", path);\n\n  path = \"/../..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/../..\", path);\n\n  path = \"/../../\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/../..\", path);\n\n  path = \"/\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/\", path);\n\n  path = \"/foo/..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/\", path);\n\n  path = \".\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\".\", path);\n\n  path = \"./.\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\".\", path);\n\n  path = \"foo/..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\".\", path);\n\n  path = \"foo/.._bar\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo/.._bar\", path);\n}\n\n#ifdef _WIN32\nTEST(CanonicalizePath, PathSamplesWindows) {\n  string path;\n\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"\", path);\n\n  path = \"foo.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo.h\", path);\n\n  path = \".\\\\foo.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo.h\", path);\n\n  path = \".\\\\foo\\\\.\\\\bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo/bar.h\", path);\n\n  path = \".\\\\x\\\\foo\\\\..\\\\bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"x/bar.h\", path);\n\n  path = \".\\\\x\\\\foo\\\\..\\\\..\\\\bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"bar.h\", path);\n\n  path = \"foo\\\\\\\\bar\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo/bar\", path);\n\n  path = \"foo\\\\\\\\.\\\\\\\\..\\\\\\\\\\\\bar\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"bar\", path);\n\n  path = \".\\\\x\\\\..\\\\foo\\\\..\\\\..\\\\bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../bar.h\", path);\n\n  path = \"foo\\\\.\\\\.\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo\", path);\n\n  path = \"foo\\\\bar\\\\..\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo\", path);\n\n  path = \"foo\\\\.hidden_bar\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"foo/.hidden_bar\", path);\n\n  path = \"\\\\foo\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/foo\", path);\n\n  path = \"\\\\\\\\foo\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"//foo\", path);\n\n  path = \"\\\\\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/\", path);\n}\n\nTEST(CanonicalizePath, SlashTracking) {\n  string path;\n  uint64_t slash_bits;\n\n  path = \"foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"foo.h\", path);\n  EXPECT_EQ(0, slash_bits);\n\n  path = \"a\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/foo.h\", path);\n  EXPECT_EQ(1, slash_bits);\n\n  path = \"a/bcd/efh\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/bcd/efh/foo.h\", path);\n  EXPECT_EQ(4, slash_bits);\n\n  path = \"a\\\\bcd/efh\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/bcd/efh/foo.h\", path);\n  EXPECT_EQ(5, slash_bits);\n\n  path = \"a\\\\bcd\\\\efh\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/bcd/efh/foo.h\", path);\n  EXPECT_EQ(7, slash_bits);\n\n  path = \"a/bcd/efh/foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/bcd/efh/foo.h\", path);\n  EXPECT_EQ(0, slash_bits);\n\n  path = \"a\\\\./efh\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/efh/foo.h\", path);\n  EXPECT_EQ(3, slash_bits);\n\n  path = \"a\\\\../efh\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"efh/foo.h\", path);\n  EXPECT_EQ(1, slash_bits);\n\n  path = \"a\\\\b\\\\c\\\\d\\\\e\\\\f\\\\g\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/b/c/d/e/f/g/foo.h\", path);\n  EXPECT_EQ(127, slash_bits);\n\n  path = \"a\\\\b\\\\c\\\\..\\\\..\\\\..\\\\g\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"g/foo.h\", path);\n  EXPECT_EQ(1, slash_bits);\n\n  path = \"a\\\\b/c\\\\../../..\\\\g\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"g/foo.h\", path);\n  EXPECT_EQ(1, slash_bits);\n\n  path = \"a\\\\b/c\\\\./../..\\\\g\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/g/foo.h\", path);\n  EXPECT_EQ(3, slash_bits);\n\n  path = \"a\\\\b/c\\\\./../..\\\\g/foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/g/foo.h\", path);\n  EXPECT_EQ(1, slash_bits);\n\n  path = \"a\\\\\\\\\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/foo.h\", path);\n  EXPECT_EQ(1, slash_bits);\n\n  path = \"a/\\\\\\\\foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/foo.h\", path);\n  EXPECT_EQ(0, slash_bits);\n\n  path = \"a\\\\//foo.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(\"a/foo.h\", path);\n  EXPECT_EQ(1, slash_bits);\n}\n\nTEST(CanonicalizePath, CanonicalizeNotExceedingLen) {\n  // Make sure searching \\/ doesn't go past supplied len.\n  char buf[] = \"foo/bar\\\\baz.h\\\\\";  // Last \\ past end.\n  uint64_t slash_bits;\n  size_t size = 13;\n  ::CanonicalizePath(buf, &size, &slash_bits);\n  EXPECT_EQ(0, strncmp(\"foo/bar/baz.h\", buf, size));\n  EXPECT_EQ(2, slash_bits);  // Not including the trailing one.\n}\n\nTEST(CanonicalizePath, TooManyComponents) {\n  string path;\n  uint64_t slash_bits;\n\n  // 64 is OK.\n  path = \"a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./\"\n         \"a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./x.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, 0x0);\n\n  // Backslashes version.\n  path =\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\\"\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\\"\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\\"\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\x.h\";\n\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, 0xffffffff);\n\n  // 65 is OK if #component is less than 60 after path canonicalization.\n  path = \"a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./\"\n         \"a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./x/y.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, 0x0);\n\n  // Backslashes version.\n  path =\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\\"\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\\"\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\\"\n      \"a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\a\\\\.\\\\x\\\\y.h\";\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, uint64_t(0x1ffffffff));\n\n\n  // 59 after canonicalization is OK.\n  path = \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n         \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/x/y.h\";\n  EXPECT_EQ(58, std::count(path.begin(), path.end(), '/'));\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, 0x0);\n\n  // Backslashes version.\n  path =\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\x\\\\y.h\";\n  EXPECT_EQ(58, std::count(path.begin(), path.end(), '\\\\'));\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, uint64_t(0x3ffffffffffffff));\n\n  // More than 60 components is now completely ok too.\n  path =\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\\"\n      \"a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\a\\\\x\\\\y.h\";\n  EXPECT_EQ(218, std::count(path.begin(), path.end(), '\\\\'));\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, 0xffffffffffffffff);\n}\n#else   // !_WIN32\nTEST(CanonicalizePath, TooManyComponents) {\n  string path;\n  uint64_t slash_bits;\n\n  // More than 60 components is now completely ok.\n  path =\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/\"\n      \"a/a/a/a/a/a/a/a/a/x/y.h\";\n  EXPECT_EQ(218, std::count(path.begin(), path.end(), '/'));\n  CanonicalizePath(&path, &slash_bits);\n  EXPECT_EQ(slash_bits, 0x0);\n}\n#endif  // !_WIN32\n\nTEST(CanonicalizePath, UpDir) {\n  string path, err;\n  path = \"../../foo/bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../../foo/bar.h\", path);\n\n  path = \"test/../../foo/bar.h\";\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"../foo/bar.h\", path);\n}\n\nTEST(CanonicalizePath, AbsolutePath) {\n  string path = \"/usr/include/stdio.h\";\n  string err;\n  CanonicalizePath(&path);\n  EXPECT_EQ(\"/usr/include/stdio.h\", path);\n}\n\nTEST(CanonicalizePath, NotNullTerminated) {\n  string path;\n  size_t len;\n  uint64_t unused;\n\n  path = \"foo/. bar/.\";\n  len = strlen(\"foo/.\");  // Canonicalize only the part before the space.\n  CanonicalizePath(&path[0], &len, &unused);\n  EXPECT_EQ(strlen(\"foo\"), len);\n  EXPECT_EQ(\"foo/. bar/.\", string(path));\n\n  // Verify that foo/..file gets canonicalized to 'file' without\n  // touching the rest of the string.\n  path = \"foo/../file bar/.\";\n  len = strlen(\"foo/../file\");\n  CanonicalizePath(&path[0], &len, &unused);\n  EXPECT_EQ(strlen(\"file\"), len);\n  EXPECT_EQ(\"file../file bar/.\", string(path));\n}\n\nTEST(PathEscaping, TortureTest) {\n  string result;\n\n  GetWin32EscapedString(\"foo bar\\\\\\\"'$@d!st!c'\\\\path'\\\\\", &result);\n  EXPECT_EQ(\"\\\"foo bar\\\\\\\\\\\\\\\"'$@d!st!c'\\\\path'\\\\\\\\\\\"\", result);\n  result.clear();\n\n  GetShellEscapedString(\"foo bar\\\"/'$@d!st!c'/path'\", &result);\n  EXPECT_EQ(\"'foo bar\\\"/'\\\\''$@d!st!c'\\\\''/path'\\\\'''\", result);\n}\n\nTEST(PathEscaping, SensiblePathsAreNotNeedlesslyEscaped) {\n  const char* path = \"some/sensible/path/without/crazy/characters.c++\";\n  string result;\n\n  GetWin32EscapedString(path, &result);\n  EXPECT_EQ(path, result);\n  result.clear();\n\n  GetShellEscapedString(path, &result);\n  EXPECT_EQ(path, result);\n}\n\nTEST(PathEscaping, SensibleWin32PathsAreNotNeedlesslyEscaped) {\n  const char* path = \"some\\\\sensible\\\\path\\\\without\\\\crazy\\\\characters.c++\";\n  string result;\n\n  GetWin32EscapedString(path, &result);\n  EXPECT_EQ(path, result);\n}\n\nTEST(StripAnsiEscapeCodes, EscapeAtEnd) {\n  string stripped = StripAnsiEscapeCodes(\"foo\\33\");\n  EXPECT_EQ(\"foo\", stripped);\n\n  stripped = StripAnsiEscapeCodes(\"foo\\33[\");\n  EXPECT_EQ(\"foo\", stripped);\n}\n\nTEST(StripAnsiEscapeCodes, StripColors) {\n  // An actual clang warning.\n  string input = \"\\33[1maffixmgr.cxx:286:15: \\33[0m\\33[0;1;35mwarning: \"\n                 \"\\33[0m\\33[1musing the result... [-Wparentheses]\\33[0m\";\n  string stripped = StripAnsiEscapeCodes(input);\n  EXPECT_EQ(\"affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]\",\n            stripped);\n}\n"
  },
  {
    "path": "src/version.cc",
    "content": "// Copyright 2013 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"version.h\"\n\n#include <stdlib.h>\n\n#include \"util.h\"\n\nusing namespace std;\n\nconst char* kNinjaVersion = \"1.14.0.git\";\n\nvoid ParseVersion(const string& version, int* major, int* minor) {\n  size_t end = version.find('.');\n  *major = atoi(version.substr(0, end).c_str());\n  *minor = 0;\n  if (end != string::npos) {\n    size_t start = end + 1;\n    end = version.find('.', start);\n    *minor = atoi(version.substr(start, end).c_str());\n  }\n}\n\nvoid CheckNinjaVersion(const string& version, int* file_major,\n                       int* file_minor) {\n  int bin_major, bin_minor;\n  ParseVersion(kNinjaVersion, &bin_major, &bin_minor);\n  ParseVersion(version, file_major, file_minor);\n\n  if (bin_major > *file_major) {\n    Warning(\"ninja executable version (%s) greater than build file \"\n            \"ninja_required_version (%s); versions may be incompatible.\",\n            kNinjaVersion, version.c_str());\n    return;\n  }\n\n  if ((bin_major == *file_major && bin_minor < *file_minor) ||\n      bin_major < *file_major) {\n    Fatal(\"ninja version (%s) incompatible with build file \"\n          \"ninja_required_version version (%s).\",\n          kNinjaVersion, version.c_str());\n  }\n}\n"
  },
  {
    "path": "src/version.h",
    "content": "// Copyright 2013 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_VERSION_H_\n#define NINJA_VERSION_H_\n\n#include <string>\n\n/// The version number of the current Ninja release.  This will always\n/// be \"git\" on trunk.\nextern const char* kNinjaVersion;\n\n/// Parse the major/minor components of a version string.\nvoid ParseVersion(const std::string& version, int* major, int* minor);\n\n/// Check whether \\a version is compatible with the current Ninja version,\n/// aborting if not.\nvoid CheckNinjaVersion(const std::string& required_version, int* file_major,\n                       int* file_minor);\n\n#endif  // NINJA_VERSION_H_\n"
  },
  {
    "path": "src/win32port.h",
    "content": "// Copyright 2012 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef NINJA_WIN32PORT_H_\n#define NINJA_WIN32PORT_H_\n\n#if defined(__MINGW32__) || defined(__MINGW64__)\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n#include <inttypes.h>\n#endif\n\ntypedef signed short int16_t;\ntypedef unsigned short uint16_t;\n/// A 64-bit integer type\ntypedef signed long long int64_t;\ntypedef unsigned long long uint64_t;\n\n// printf format specifier for uint64_t, from C99.\n#ifndef PRIu64\n#define PRId64 \"I64d\"\n#define PRIu64 \"I64u\"\n#define PRIx64 \"I64x\"\n#endif\n\n#endif // NINJA_WIN32PORT_H_\n\n"
  },
  {
    "path": "tests/restat/test_restat_builddir.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"Integration test for 'ninja -t restat' with builddir.\"\"\"\n\nimport os\nimport subprocess\nimport tempfile\nimport time\nimport unittest\n\nNINJA_PATH = os.path.abspath(\"./ninja\")\n\n\nclass RestatBuildDirTest(unittest.TestCase):\n    \"\"\"Test that 'ninja -t restat' respects builddir.\"\"\"\n\n    def setUp(self):\n        \"\"\"Create a temporary directory for the test.\"\"\"\n        self.test_dir = tempfile.mkdtemp(prefix=\"ninja_restat_test_\")\n        self.original_dir = os.getcwd()\n        os.chdir(self.test_dir)\n\n    def tearDown(self):\n        \"\"\"Clean up the temporary directory.\"\"\"\n        os.chdir(self.original_dir)\n        import shutil\n\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_restat_with_builddir(self):\n        \"\"\"Test that ninja -t restat updates mtime in builddir/.ninja_log.\"\"\"\n\n        # Create a simple build.ninja file with builddir\n        build_ninja = \"\"\"\nbuilddir = build\n\nrule touch\n  command = touch $out\n  description = Creating $out\n\nbuild output.txt: touch\n\"\"\"\n        with open(\"build.ninja\", \"w\") as f:\n            f.write(build_ninja)\n\n        # Run ninja to build the output\n        result = subprocess.run([NINJA_PATH], capture_output=True, text=True)\n        self.assertEqual(result.returncode, 0, f\"Initial build failed: {result.stderr}\")\n        self.assertTrue(os.path.exists(\"output.txt\"), \"output.txt was not created\")\n        self.assertTrue(\n            os.path.exists(\"build/.ninja_log\"), \"build/.ninja_log was not created\"\n        )\n\n        # Read the original .ninja_log to get the initial mtime\n        with open(\"build/.ninja_log\", \"r\") as f:\n            log_lines = f.readlines()\n\n        # Find the entry for output.txt\n        output_entry = \"\"\n        for line in log_lines:\n            if \"output.txt\" in line:\n                output_entry = line.strip()\n                break\n\n        self.assertNotEqual(\n            output_entry, \"\", \"output.txt not found in build/.ninja_log\"\n        )\n\n        # Parse the log entry: start_time\\tend_time\\tmtime\\toutput\\tcommand_hash\n        parts = output_entry.split(\"\\t\")\n        self.assertEqual(len(parts), 5, f\"Unexpected log format: {output_entry}\")\n        original_mtime = int(parts[2])\n\n        # Wait a bit to ensure different mtime\n        time.sleep(0.01)\n\n        # Touch the output file to update its mtime using explicit time\n        current_time = time.time() + 2  # Add 2 seconds to ensure different mtime\n        os.utime(\"output.txt\", (current_time, current_time))\n\n        # Get the new actual file mtime\n        new_file_mtime = int(\n            os.path.getmtime(\"output.txt\") * 1000000000\n        )  # Convert to nanoseconds\n        self.assertGreater(\n            new_file_mtime,\n            original_mtime,\n            f\"File mtime should have increased: {new_file_mtime} vs {original_mtime}\",\n        )\n\n        # Run ninja -t restat\n        result = subprocess.run(\n            [NINJA_PATH, \"-t\", \"restat\", \"--builddir=build\"],\n            capture_output=True,\n            text=True,\n        )\n        self.assertEqual(\n            result.returncode, 0, f\"ninja -t restat failed: {result.stderr}\"\n        )\n\n        # Read the updated .ninja_log\n        with open(\"build/.ninja_log\", \"r\") as f:\n            updated_log_lines = f.readlines()\n\n        # Find the updated entry for output.txt\n        updated_entry = \"\"\n        for line in updated_log_lines:\n            if \"output.txt\" in line:\n                updated_entry = line.strip()\n                break\n\n        self.assertNotEqual(\n            updated_entry, \"\", \"output.txt not found in updated build/.ninja_log\"\n        )\n\n        # Parse the updated log entry\n        updated_parts = updated_entry.split(\"\\t\")\n        self.assertEqual(\n            len(updated_parts), 5, f\"Unexpected updated log format: {updated_entry}\"\n        )\n        updated_mtime = int(updated_parts[2])\n\n        # Verify that the mtime was updated in the log\n        self.assertGreater(\n            updated_mtime,\n            original_mtime,\n            f\"mtime in build/.ninja_log should have been updated. \"\n            f\"Original: {original_mtime}, Updated: {updated_mtime}\",\n        )\n"
  },
  {
    "path": "windows/ninja.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <application>\n    <windowsSettings>\n      <activeCodePage xmlns=\"http://schemas.microsoft.com/SMI/2019/WindowsSettings\">UTF-8</activeCodePage>\n      <longPathAware  xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">true</longPathAware>\n    </windowsSettings>\n  </application>\n</assembly>\n"
  }
]