From 753f0f2a787cdace846371e3679f5e69d2f66a44 Mon Sep 17 00:00:00 2001 From: Patricia Aas Date: Wed, 6 Jan 2021 15:08:18 +0100 Subject: [PATCH 01/51] Set the module to point to turtlebrowsers chromium repo --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index be62b2203e7..8abf8f2ac58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "src/3rdparty"] path = src/3rdparty - url = ../qtwebengine-chromium.git + url = ../chromium.git ignore = dirty initrepo = true From d5d3a00143d012021892a5d7de0b7f3d3370a1c9 Mon Sep 17 00:00:00 2001 From: Patricia Aas Date: Thu, 7 Jan 2021 11:54:07 +0100 Subject: [PATCH 02/51] Un-gitmodule --- .gitmodules | 5 ----- src/3rdparty | 1 - 2 files changed, 6 deletions(-) delete mode 160000 src/3rdparty diff --git a/.gitmodules b/.gitmodules index 8abf8f2ac58..e69de29bb2d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +0,0 @@ -[submodule "src/3rdparty"] - path = src/3rdparty - url = ../chromium.git - ignore = dirty - initrepo = true diff --git a/src/3rdparty b/src/3rdparty deleted file mode 160000 index fb6ab5e4838..00000000000 --- a/src/3rdparty +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fb6ab5e483876298235be1c6a6013b426c82b759 From 49a2c84ea5cdbac10e1e970fc325d17481de035c Mon Sep 17 00:00:00 2001 From: Patricia Aas Date: Thu, 7 Jan 2021 11:54:34 +0100 Subject: [PATCH 03/51] Add ninja --- src/3rdparty/ninja/.clang-format | 25 + src/3rdparty/ninja/.travis.yml | 6 + src/3rdparty/ninja/COPYING | 202 ++ src/3rdparty/ninja/HACKING.md | 224 ++ src/3rdparty/ninja/README | 21 + src/3rdparty/ninja/RELEASING | 32 + src/3rdparty/ninja/appveyor.yml | 40 + src/3rdparty/ninja/bootstrap.py | 23 + src/3rdparty/ninja/configure.py | 684 +++++ src/3rdparty/ninja/doc/README.md | 11 + src/3rdparty/ninja/doc/dblatex.xsl | 7 + src/3rdparty/ninja/doc/docbook.xsl | 31 + src/3rdparty/ninja/doc/doxygen.config | 1250 +++++++++ src/3rdparty/ninja/doc/manual.asciidoc | 1006 +++++++ src/3rdparty/ninja/doc/style.css | 29 + .../ninja/misc/afl-fuzz-tokens/kw_build | 1 + .../ninja/misc/afl-fuzz-tokens/kw_default | 1 + .../ninja/misc/afl-fuzz-tokens/kw_include | 1 + .../ninja/misc/afl-fuzz-tokens/kw_pool | 1 + .../ninja/misc/afl-fuzz-tokens/kw_rule | 1 + .../ninja/misc/afl-fuzz-tokens/kw_subninja | 1 + .../ninja/misc/afl-fuzz-tokens/misc_a | 1 + .../ninja/misc/afl-fuzz-tokens/misc_b | 1 + .../ninja/misc/afl-fuzz-tokens/misc_colon | 1 + .../ninja/misc/afl-fuzz-tokens/misc_cont | 1 + .../ninja/misc/afl-fuzz-tokens/misc_dollar | 1 + .../ninja/misc/afl-fuzz-tokens/misc_eq | 1 + .../ninja/misc/afl-fuzz-tokens/misc_indent | 1 + .../ninja/misc/afl-fuzz-tokens/misc_pipe | 1 + .../ninja/misc/afl-fuzz-tokens/misc_pipepipe | 1 + .../ninja/misc/afl-fuzz-tokens/misc_space | 1 + src/3rdparty/ninja/misc/afl-fuzz/build.ninja | 5 + src/3rdparty/ninja/misc/bash-completion | 57 + src/3rdparty/ninja/misc/inherited-fds.ninja | 23 + src/3rdparty/ninja/misc/long-slow-build.ninja | 38 + src/3rdparty/ninja/misc/measure.py | 56 + src/3rdparty/ninja/misc/ninja-mode.el | 85 + src/3rdparty/ninja/misc/ninja.vim | 83 + src/3rdparty/ninja/misc/ninja_syntax.py | 181 ++ src/3rdparty/ninja/misc/ninja_syntax_test.py | 191 ++ src/3rdparty/ninja/misc/output_test.py | 103 + src/3rdparty/ninja/misc/packaging/ninja.spec | 42 + src/3rdparty/ninja/misc/packaging/rpmbuild.sh | 29 + .../ninja/misc/write_fake_manifests.py | 272 ++ src/3rdparty/ninja/misc/zsh-completion | 72 + src/3rdparty/ninja/src/browse.cc | 73 + src/3rdparty/ninja/src/browse.h | 28 + src/3rdparty/ninja/src/browse.py | 224 ++ src/3rdparty/ninja/src/build.cc | 929 +++++++ src/3rdparty/ninja/src/build.h | 295 +++ src/3rdparty/ninja/src/build_log.cc | 408 +++ src/3rdparty/ninja/src/build_log.h | 93 + src/3rdparty/ninja/src/build_log_perftest.cc | 149 ++ src/3rdparty/ninja/src/build_log_test.cc | 307 +++ src/3rdparty/ninja/src/build_test.cc | 2310 +++++++++++++++++ src/3rdparty/ninja/src/canon_perftest.cc | 57 + src/3rdparty/ninja/src/clean.cc | 263 ++ src/3rdparty/ninja/src/clean.h | 107 + src/3rdparty/ninja/src/clean_test.cc | 407 +++ src/3rdparty/ninja/src/clparser.cc | 126 + src/3rdparty/ninja/src/clparser.h | 52 + src/3rdparty/ninja/src/clparser_perftest.cc | 157 ++ src/3rdparty/ninja/src/clparser_test.cc | 117 + src/3rdparty/ninja/src/debug_flags.cc | 21 + src/3rdparty/ninja/src/debug_flags.h | 33 + src/3rdparty/ninja/src/depfile_parser.cc | 242 ++ src/3rdparty/ninja/src/depfile_parser.h | 35 + src/3rdparty/ninja/src/depfile_parser.in.cc | 120 + .../ninja/src/depfile_parser_perftest.cc | 77 + src/3rdparty/ninja/src/depfile_parser_test.cc | 157 ++ src/3rdparty/ninja/src/deps_log.cc | 410 +++ src/3rdparty/ninja/src/deps_log.h | 121 + src/3rdparty/ninja/src/deps_log_test.cc | 479 ++++ src/3rdparty/ninja/src/disk_interface.cc | 264 ++ src/3rdparty/ninja/src/disk_interface.h | 100 + src/3rdparty/ninja/src/disk_interface_test.cc | 311 +++ src/3rdparty/ninja/src/edit_distance.cc | 69 + src/3rdparty/ninja/src/edit_distance.h | 25 + src/3rdparty/ninja/src/edit_distance_test.cc | 48 + src/3rdparty/ninja/src/eval_env.cc | 132 + src/3rdparty/ninja/src/eval_env.h | 105 + src/3rdparty/ninja/src/exit_status.h | 24 + .../ninja/src/gen_doxygen_mainpage.sh | 92 + src/3rdparty/ninja/src/getopt.c | 410 +++ src/3rdparty/ninja/src/getopt.h | 57 + src/3rdparty/ninja/src/graph.cc | 589 +++++ src/3rdparty/ninja/src/graph.h | 294 +++ src/3rdparty/ninja/src/graph_test.cc | 481 ++++ src/3rdparty/ninja/src/graphviz.cc | 80 + src/3rdparty/ninja/src/graphviz.h | 33 + .../ninja/src/hash_collision_bench.cc | 63 + src/3rdparty/ninja/src/hash_map.h | 120 + .../ninja/src/includes_normalize-win32.cc | 176 ++ src/3rdparty/ninja/src/includes_normalize.h | 39 + .../ninja/src/includes_normalize_test.cc | 140 + src/3rdparty/ninja/src/inline.sh | 25 + src/3rdparty/ninja/src/lexer.cc | 867 +++++++ src/3rdparty/ninja/src/lexer.h | 105 + src/3rdparty/ninja/src/lexer.in.cc | 273 ++ src/3rdparty/ninja/src/lexer_test.cc | 96 + src/3rdparty/ninja/src/line_printer.cc | 141 + src/3rdparty/ninja/src/line_printer.h | 72 + src/3rdparty/ninja/src/manifest_parser.cc | 447 ++++ src/3rdparty/ninja/src/manifest_parser.h | 87 + .../ninja/src/manifest_parser_perftest.cc | 118 + .../ninja/src/manifest_parser_test.cc | 1079 ++++++++ src/3rdparty/ninja/src/metrics.cc | 127 + src/3rdparty/ninja/src/metrics.h | 92 + src/3rdparty/ninja/src/minidump-win32.cc | 87 + src/3rdparty/ninja/src/msvc_helper-win32.cc | 106 + src/3rdparty/ninja/src/msvc_helper.h | 33 + .../ninja/src/msvc_helper_main-win32.cc | 148 ++ src/3rdparty/ninja/src/msvc_helper_test.cc | 39 + src/3rdparty/ninja/src/ninja.cc | 1228 +++++++++ src/3rdparty/ninja/src/ninja_test.cc | 160 ++ src/3rdparty/ninja/src/state.cc | 212 ++ src/3rdparty/ninja/src/state.h | 131 + src/3rdparty/ninja/src/state_test.cc | 46 + src/3rdparty/ninja/src/string_piece.h | 71 + src/3rdparty/ninja/src/string_piece_util.cc | 78 + src/3rdparty/ninja/src/string_piece_util.h | 34 + .../ninja/src/string_piece_util_test.cc | 129 + src/3rdparty/ninja/src/subprocess-posix.cc | 338 +++ src/3rdparty/ninja/src/subprocess-win32.cc | 292 +++ src/3rdparty/ninja/src/subprocess.h | 114 + src/3rdparty/ninja/src/subprocess_test.cc | 261 ++ src/3rdparty/ninja/src/test.cc | 235 ++ src/3rdparty/ninja/src/test.h | 184 ++ src/3rdparty/ninja/src/timestamp.h | 24 + src/3rdparty/ninja/src/util.cc | 606 +++++ src/3rdparty/ninja/src/util.h | 111 + src/3rdparty/ninja/src/util_test.cc | 428 +++ src/3rdparty/ninja/src/version.cc | 53 + src/3rdparty/ninja/src/version.h | 32 + src/3rdparty/ninja/src/win32port.h | 31 + 135 files changed, 25033 insertions(+) create mode 100644 src/3rdparty/ninja/.clang-format create mode 100644 src/3rdparty/ninja/.travis.yml create mode 100644 src/3rdparty/ninja/COPYING create mode 100644 src/3rdparty/ninja/HACKING.md create mode 100644 src/3rdparty/ninja/README create mode 100644 src/3rdparty/ninja/RELEASING create mode 100644 src/3rdparty/ninja/appveyor.yml create mode 100755 src/3rdparty/ninja/bootstrap.py create mode 100755 src/3rdparty/ninja/configure.py create mode 100644 src/3rdparty/ninja/doc/README.md create mode 100644 src/3rdparty/ninja/doc/dblatex.xsl create mode 100644 src/3rdparty/ninja/doc/docbook.xsl create mode 100644 src/3rdparty/ninja/doc/doxygen.config create mode 100644 src/3rdparty/ninja/doc/manual.asciidoc create mode 100644 src/3rdparty/ninja/doc/style.css create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_build create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_default create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_include create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_pool create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_rule create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_subninja create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_a create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_b create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_colon create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_cont create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_dollar create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_eq create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_indent create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipe create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipepipe create mode 100644 src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_space create mode 100644 src/3rdparty/ninja/misc/afl-fuzz/build.ninja create mode 100644 src/3rdparty/ninja/misc/bash-completion create mode 100644 src/3rdparty/ninja/misc/inherited-fds.ninja create mode 100644 src/3rdparty/ninja/misc/long-slow-build.ninja create mode 100755 src/3rdparty/ninja/misc/measure.py create mode 100644 src/3rdparty/ninja/misc/ninja-mode.el create mode 100644 src/3rdparty/ninja/misc/ninja.vim create mode 100644 src/3rdparty/ninja/misc/ninja_syntax.py create mode 100755 src/3rdparty/ninja/misc/ninja_syntax_test.py create mode 100755 src/3rdparty/ninja/misc/output_test.py create mode 100644 src/3rdparty/ninja/misc/packaging/ninja.spec create mode 100755 src/3rdparty/ninja/misc/packaging/rpmbuild.sh create mode 100644 src/3rdparty/ninja/misc/write_fake_manifests.py create mode 100644 src/3rdparty/ninja/misc/zsh-completion create mode 100644 src/3rdparty/ninja/src/browse.cc create mode 100644 src/3rdparty/ninja/src/browse.h create mode 100755 src/3rdparty/ninja/src/browse.py create mode 100644 src/3rdparty/ninja/src/build.cc create mode 100644 src/3rdparty/ninja/src/build.h create mode 100644 src/3rdparty/ninja/src/build_log.cc create mode 100644 src/3rdparty/ninja/src/build_log.h create mode 100644 src/3rdparty/ninja/src/build_log_perftest.cc create mode 100644 src/3rdparty/ninja/src/build_log_test.cc create mode 100644 src/3rdparty/ninja/src/build_test.cc create mode 100644 src/3rdparty/ninja/src/canon_perftest.cc create mode 100644 src/3rdparty/ninja/src/clean.cc create mode 100644 src/3rdparty/ninja/src/clean.h create mode 100644 src/3rdparty/ninja/src/clean_test.cc create mode 100644 src/3rdparty/ninja/src/clparser.cc create mode 100644 src/3rdparty/ninja/src/clparser.h create mode 100644 src/3rdparty/ninja/src/clparser_perftest.cc create mode 100644 src/3rdparty/ninja/src/clparser_test.cc create mode 100644 src/3rdparty/ninja/src/debug_flags.cc create mode 100644 src/3rdparty/ninja/src/debug_flags.h create mode 100644 src/3rdparty/ninja/src/depfile_parser.cc create mode 100644 src/3rdparty/ninja/src/depfile_parser.h create mode 100644 src/3rdparty/ninja/src/depfile_parser.in.cc create mode 100644 src/3rdparty/ninja/src/depfile_parser_perftest.cc create mode 100644 src/3rdparty/ninja/src/depfile_parser_test.cc create mode 100644 src/3rdparty/ninja/src/deps_log.cc create mode 100644 src/3rdparty/ninja/src/deps_log.h create mode 100644 src/3rdparty/ninja/src/deps_log_test.cc create mode 100644 src/3rdparty/ninja/src/disk_interface.cc create mode 100644 src/3rdparty/ninja/src/disk_interface.h create mode 100644 src/3rdparty/ninja/src/disk_interface_test.cc create mode 100644 src/3rdparty/ninja/src/edit_distance.cc create mode 100644 src/3rdparty/ninja/src/edit_distance.h create mode 100644 src/3rdparty/ninja/src/edit_distance_test.cc create mode 100644 src/3rdparty/ninja/src/eval_env.cc create mode 100644 src/3rdparty/ninja/src/eval_env.h create mode 100644 src/3rdparty/ninja/src/exit_status.h create mode 100755 src/3rdparty/ninja/src/gen_doxygen_mainpage.sh create mode 100644 src/3rdparty/ninja/src/getopt.c create mode 100644 src/3rdparty/ninja/src/getopt.h create mode 100644 src/3rdparty/ninja/src/graph.cc create mode 100644 src/3rdparty/ninja/src/graph.h create mode 100644 src/3rdparty/ninja/src/graph_test.cc create mode 100644 src/3rdparty/ninja/src/graphviz.cc create mode 100644 src/3rdparty/ninja/src/graphviz.h create mode 100644 src/3rdparty/ninja/src/hash_collision_bench.cc create mode 100644 src/3rdparty/ninja/src/hash_map.h create mode 100644 src/3rdparty/ninja/src/includes_normalize-win32.cc create mode 100644 src/3rdparty/ninja/src/includes_normalize.h create mode 100644 src/3rdparty/ninja/src/includes_normalize_test.cc create mode 100755 src/3rdparty/ninja/src/inline.sh create mode 100644 src/3rdparty/ninja/src/lexer.cc create mode 100644 src/3rdparty/ninja/src/lexer.h create mode 100644 src/3rdparty/ninja/src/lexer.in.cc create mode 100644 src/3rdparty/ninja/src/lexer_test.cc create mode 100644 src/3rdparty/ninja/src/line_printer.cc create mode 100644 src/3rdparty/ninja/src/line_printer.h create mode 100644 src/3rdparty/ninja/src/manifest_parser.cc create mode 100644 src/3rdparty/ninja/src/manifest_parser.h create mode 100644 src/3rdparty/ninja/src/manifest_parser_perftest.cc create mode 100644 src/3rdparty/ninja/src/manifest_parser_test.cc create mode 100644 src/3rdparty/ninja/src/metrics.cc create mode 100644 src/3rdparty/ninja/src/metrics.h create mode 100644 src/3rdparty/ninja/src/minidump-win32.cc create mode 100644 src/3rdparty/ninja/src/msvc_helper-win32.cc create mode 100644 src/3rdparty/ninja/src/msvc_helper.h create mode 100644 src/3rdparty/ninja/src/msvc_helper_main-win32.cc create mode 100644 src/3rdparty/ninja/src/msvc_helper_test.cc create mode 100644 src/3rdparty/ninja/src/ninja.cc create mode 100644 src/3rdparty/ninja/src/ninja_test.cc create mode 100644 src/3rdparty/ninja/src/state.cc create mode 100644 src/3rdparty/ninja/src/state.h create mode 100644 src/3rdparty/ninja/src/state_test.cc create mode 100644 src/3rdparty/ninja/src/string_piece.h create mode 100644 src/3rdparty/ninja/src/string_piece_util.cc create mode 100644 src/3rdparty/ninja/src/string_piece_util.h create mode 100644 src/3rdparty/ninja/src/string_piece_util_test.cc create mode 100644 src/3rdparty/ninja/src/subprocess-posix.cc create mode 100644 src/3rdparty/ninja/src/subprocess-win32.cc create mode 100644 src/3rdparty/ninja/src/subprocess.h create mode 100644 src/3rdparty/ninja/src/subprocess_test.cc create mode 100644 src/3rdparty/ninja/src/test.cc create mode 100644 src/3rdparty/ninja/src/test.h create mode 100644 src/3rdparty/ninja/src/timestamp.h create mode 100644 src/3rdparty/ninja/src/util.cc create mode 100644 src/3rdparty/ninja/src/util.h create mode 100644 src/3rdparty/ninja/src/util_test.cc create mode 100644 src/3rdparty/ninja/src/version.cc create mode 100644 src/3rdparty/ninja/src/version.h create mode 100644 src/3rdparty/ninja/src/win32port.h diff --git a/src/3rdparty/ninja/.clang-format b/src/3rdparty/ninja/.clang-format new file mode 100644 index 00000000000..1841c036f9c --- /dev/null +++ b/src/3rdparty/ninja/.clang-format @@ -0,0 +1,25 @@ +# Copyright 2014 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This isn't meant to be authoritative, but it's good enough to be useful. +# Still use your best judgement for formatting decisions: clang-format +# sometimes makes strange choices. + +BasedOnStyle: Google +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +Cpp11BracedListStyle: false +IndentCaseLabels: false diff --git a/src/3rdparty/ninja/.travis.yml b/src/3rdparty/ninja/.travis.yml new file mode 100644 index 00000000000..093139b83e6 --- /dev/null +++ b/src/3rdparty/ninja/.travis.yml @@ -0,0 +1,6 @@ +sudo: false +language: cpp +compiler: + - gcc + - clang +script: ./configure.py --bootstrap && ./ninja all && ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots && ./misc/ninja_syntax_test.py diff --git a/src/3rdparty/ninja/COPYING b/src/3rdparty/ninja/COPYING new file mode 100644 index 00000000000..131cb1da46d --- /dev/null +++ b/src/3rdparty/ninja/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2010 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/3rdparty/ninja/HACKING.md b/src/3rdparty/ninja/HACKING.md new file mode 100644 index 00000000000..e7c91efdf5d --- /dev/null +++ b/src/3rdparty/ninja/HACKING.md @@ -0,0 +1,224 @@ +## Basic overview + +`./configure.py` generates the `build.ninja` files used to build +ninja. It accepts various flags to adjust build parameters. +Run './configure.py --help' for more configuration options. + +The primary build target of interest is `ninja`, but when hacking on +Ninja your changes should be testable so it's more useful to build and +run `ninja_test` when developing. + +### Bootstrapping + +Ninja is built using itself. To bootstrap the first binary, run the +configure script as `./configure.py --bootstrap`. This first compiles +all non-test source files together, then re-builds Ninja using itself. +You should end up with a `ninja` binary (or `ninja.exe`) in the source root. + +#### Windows + +On Windows, you'll need to install Python to run `configure.py`, and +run everything under a Visual Studio Tools Command Prompt (or after +running `vcvarsall` in a normal command prompt). See below if you +want to use mingw or some other compiler instead of Visual Studio. + +### Adjusting build flags + +Build in "debug" mode while developing (disables optimizations and builds +way faster on Windows): + + ./configure.py --debug + +To use clang, set `CXX`: + + CXX=clang++ ./configure.py + +## How to successfully make changes to Ninja + +Github pull requests are convenient for me to merge (I can just click +a button and it's all handled server-side), but I'm also comfortable +accepting pre-github git patches (via `send-email` etc.). + +Good pull requests have all of these attributes: + +* Are scoped to one specific issue +* Include a test to demonstrate their correctness +* Update the docs where relevant +* Match the Ninja coding style (see below) +* Don't include a mess of "oops, fix typo" commits + +These are typically merged without hesitation. If a change is lacking +any of the above I usually will ask you to fix it, though there are +obvious exceptions (fixing typos in comments don't need tests). + +I am very wary of changes that increase the complexity of Ninja (in +particular, new build file syntax or command-line flags) or increase +the maintenance burden of Ninja. Ninja is already successfully used +by hundreds of developers for large projects and it already achieves +(most of) the goals I set out for it to do. It's probably best to +discuss new feature ideas on the [mailing list](https://groups.google.com/forum/#!forum/ninja-build) +before I shoot down your patch. + +## Testing + +### Test-driven development + +Set your build command to + + ./ninja ninja_test && ./ninja_test --gtest_filter=MyTest.Name + +now you can repeatedly run that while developing until the tests pass +(I frequently set it as my compilation command in Emacs). Remember to +build "all" before committing to verify the other source still works! + +## Testing performance impact of changes + +If you have a Chrome build handy, it's a good test case. Otherwise, +[the github downoads page](https://github.com/ninja-build/ninja/releases) +has a copy of the Chrome build files (and depfiles). You can untar +that, then run + + path/to/my/ninja chrome + +and compare that against a baseline Ninja. + +There's a script at `misc/measure.py` that repeatedly runs a command like +the above (to address variance) and summarizes its runtime. E.g. + + path/to/misc/measure.py path/to/my/ninja chrome + +For changing the depfile parser, you can also build `parser_perftest` +and run that directly on some representative input files. + +## Coding guidelines + +Generally it's the [Google C++ coding style][], but in brief: + +* Function name are camelcase. +* Member methods are camelcase, expect for trivial getters which are + underscore separated. +* Local variables are underscore separated. +* Member variables are underscore separated and suffixed by an extra + underscore. +* Two spaces indentation. +* Opening braces is at the end of line. +* Lines are 80 columns maximum. +* All source files should have the Google Inc. license header. + +[Google C++ coding style]: https://google.github.io/styleguide/cppguide.html + +## Documentation + +### Style guidelines + +* Use `///` for doxygen. +* Use `\a` to refer to arguments. +* It's not necessary to document each argument, especially when they're + relatively self-evident (e.g. in `CanonicalizePath(string* path, string* err)`, + the arguments are hopefully obvious) + +### Building the manual + + sudo apt-get install asciidoc --no-install-recommends + ./ninja manual + +### Building the code documentation + + sudo apt-get install doxygen + ./ninja doxygen + +## Building for Windows + +While developing, it's helpful to copy `ninja.exe` to another name like +`n.exe`; otherwise, rebuilds will be unable to write `ninja.exe` because +it's locked while in use. + +### Via Visual Studio + +* Install Visual Studio (Express is fine), [Python for Windows][], + and (if making changes) googletest (see above instructions) +* In a Visual Studio command prompt: `python configure.py --bootstrap` + +[Python for Windows]: http://www.python.org/getit/windows/ + +### Via mingw on Windows (not well supported) + +* Install mingw, msys, and python +* In the mingw shell, put Python in your path, and + `python configure.py --bootstrap` +* To reconfigure, run `python configure.py` +* Remember to strip the resulting executable if size matters to you + +### Via mingw on Linux (not well supported) + +Setup on Ubuntu Lucid: +* `sudo apt-get install gcc-mingw32 wine` +* `export CC=i586-mingw32msvc-cc CXX=i586-mingw32msvc-c++ AR=i586-mingw32msvc-ar` + +Setup on Ubuntu Precise: +* `sudo apt-get install gcc-mingw-w64-i686 g++-mingw-w64-i686 wine` +* `export CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ AR=i686-w64-mingw32-ar` + +Setup on Arch: +* Uncomment the `[multilib]` section of `/etc/pacman.conf` and `sudo pacman -Sy`. +* `sudo pacman -S mingw-w64-gcc wine` +* `export CC=x86_64-w64-mingw32-cc CXX=x86_64-w64-mingw32-c++ AR=x86_64-w64-mingw32-ar` +* `export CFLAGS=-I/usr/x86_64-w64-mingw32/include` + +Then run: +* `./configure.py --platform=mingw --host=linux` +* Build `ninja.exe` using a Linux ninja binary: `/path/to/linux/ninja` +* Run: `./ninja.exe` (implicitly runs through wine(!)) + +### Using Microsoft compilers on Linux (extremely flaky) + +The trick is to install just the compilers, and not all of Visual Studio, +by following [these instructions][win7sdk]. + +[win7sdk]: http://www.kegel.com/wine/cl-howto-win7sdk.html + +### Using gcov + +Do a clean debug build with the right flags: + + CFLAGS=-coverage LDFLAGS=-coverage ./configure.py --debug + ninja -t clean ninja_test && ninja ninja_test + +Run the test binary to generate `.gcda` and `.gcno` files in the build +directory, then run gcov on the .o files to generate `.gcov` files in the +root directory: + + ./ninja_test + gcov build/*.o + +Look at the generated `.gcov` files directly, or use your favorite gcov viewer. + +### Using afl-fuzz + +Build with afl-clang++: + + CXX=path/to/afl-1.20b/afl-clang++ ./configure.py + ninja + +Then run afl-fuzz like so: + + afl-fuzz -i misc/afl-fuzz -o /tmp/afl-fuzz-out ./ninja -n -f @@ + +You can pass `-x misc/afl-fuzz-tokens` to use the token dictionary. In my +testing, that did not seem more effective though. + +#### Using afl-fuzz with asan + +If you want to use asan (the `isysroot` bit is only needed on OS X; if clang +can't find C++ standard headers make sure your LLVM checkout includes a libc++ +checkout and has libc++ installed in the build directory): + + CFLAGS="-fsanitize=address -isysroot $(xcrun -show-sdk-path)" \ + LDFLAGS=-fsanitize=address CXX=path/to/afl-1.20b/afl-clang++ \ + ./configure.py + AFL_CXX=path/to/clang++ ninja + +Make sure ninja can find the asan runtime: + + DYLD_LIBRARY_PATH=path/to//lib/clang/3.7.0/lib/darwin/ \ + afl-fuzz -i misc/afl-fuzz -o /tmp/afl-fuzz-out ./ninja -n -f @@ diff --git a/src/3rdparty/ninja/README b/src/3rdparty/ninja/README new file mode 100644 index 00000000000..a1535ffac83 --- /dev/null +++ b/src/3rdparty/ninja/README @@ -0,0 +1,21 @@ +Ninja is a small build system with a focus on speed. +https://ninja-build.org/ + +See the manual -- https://ninja-build.org/manual.html or +doc/manual.asciidoc included in the distribution -- for background +and more details. + +Binaries for Linux, Mac, and Windows are available at + https://github.com/ninja-build/ninja/releases +Run './ninja -h' for Ninja help. + +To build your own binary, on many platforms it should be sufficient to +just run `./configure.py --bootstrap`; for more details see HACKING.md. +(Also read that before making changes to Ninja, as it has advice.) + +Installation is not necessary because the only required file is the +resulting ninja binary. However, to enable features like Bash +completion and Emacs and Vim editing modes, some files in misc/ must be +copied to appropriate locations. + +If you're interested in making changes to Ninja, read HACKING.md first. diff --git a/src/3rdparty/ninja/RELEASING b/src/3rdparty/ninja/RELEASING new file mode 100644 index 00000000000..5f51b736e35 --- /dev/null +++ b/src/3rdparty/ninja/RELEASING @@ -0,0 +1,32 @@ +Notes to myself on all the steps to make for a Ninja release. + +Push new release branch: +1. Consider sending a heads-up to the ninja-build mailing list first +2. Make sure branches 'master' and 'release' are synced up locally +3. update src/version.cc with new version (with ".git"), then + git commit -am 'mark this 1.5.0.git' +4. git checkout release; git merge master +5. fix version number in src/version.cc (it will likely conflict in the above) +6. fix version in doc/manual.asciidoc (exists only on release branch) +7. commit, tag, push (don't forget to push --tags) + git commit -am v1.5.0; git push origin release + git tag v1.5.0; git push --tags + # Push the 1.5.0.git change on master too: + git checkout master; git push origin master +8. construct release notes from prior notes + credits: git shortlog -s --no-merges REV.. + +Release on github: +1. https://github.com/blog/1547-release-your-software + Add binaries to https://github.com/ninja-build/ninja/releases + +Make announcement on mailing list: +1. copy old mail + +Update website: +1. Make sure your ninja checkout is on the v1.5.0 tag +2. Clone https://github.com/ninja-build/ninja-build.github.io +3. In that repo, `./update-docs.sh` +4. Update index.html with newest version and link to release notes +5. git commit -m 'run update-docs.sh, 1.5.0 release' +6. git push origin master diff --git a/src/3rdparty/ninja/appveyor.yml b/src/3rdparty/ninja/appveyor.yml new file mode 100644 index 00000000000..4c64f291d56 --- /dev/null +++ b/src/3rdparty/ninja/appveyor.yml @@ -0,0 +1,40 @@ +version: 1.0.{build} +image: Visual Studio 2017 + +environment: + CLICOLOR_FORCE: 1 + CHERE_INVOKING: 1 # Tell Bash to inherit the current working directory + matrix: + - MSYSTEM: MINGW64 + - MSYSTEM: MSVC + +for: + - + matrix: + only: + - MSYSTEM: MINGW64 + build_script: + ps: "C:\\msys64\\usr\\bin\\bash -lc @\"\n + pacman -S --quiet --noconfirm --needed re2c 2>&1\n + sed -i 's|cmd /c $ar cqs $out.tmp $in && move /Y $out.tmp $out|$ar crs $out $in|g' configure.py\n + ./configure.py --bootstrap --platform mingw 2>&1\n + ./ninja all\n + ./ninja_test 2>&1\n + ./misc/ninja_syntax_test.py 2>&1\n\"@" + - + matrix: + only: + - MSYSTEM: MSVC + build_script: + - cmd: >- + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" + + python configure.py --bootstrap + + ninja.bootstrap.exe all + + ninja_test + + python misc/ninja_syntax_test.py + +test: off diff --git a/src/3rdparty/ninja/bootstrap.py b/src/3rdparty/ninja/bootstrap.py new file mode 100755 index 00000000000..56eab64d18d --- /dev/null +++ b/src/3rdparty/ninja/bootstrap.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import subprocess +import sys + +print('DEPRECATED: this script will be deleted.') +print('use "configure.py --bootstrap" instead.') +subprocess.check_call([sys.executable, 'configure.py', '--bootstrap']) diff --git a/src/3rdparty/ninja/configure.py b/src/3rdparty/ninja/configure.py new file mode 100755 index 00000000000..a4437489426 --- /dev/null +++ b/src/3rdparty/ninja/configure.py @@ -0,0 +1,684 @@ +#!/usr/bin/env python +# +# Copyright 2001 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Script that generates the build.ninja for ninja itself. + +Projects that use ninja themselves should either write a similar script +or use a meta-build system that supports Ninja output.""" + +from __future__ import print_function + +from optparse import OptionParser +import os +import pipes +import string +import subprocess +import sys + +sourcedir = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, os.path.join(sourcedir, 'misc')) +import ninja_syntax + + +class Platform(object): + """Represents a host/target platform and its specific build attributes.""" + def __init__(self, platform): + self._platform = platform + if self._platform is not None: + return + self._platform = sys.platform + if self._platform.startswith('linux'): + self._platform = 'linux' + elif self._platform.startswith('freebsd'): + self._platform = 'freebsd' + elif self._platform.startswith('gnukfreebsd'): + self._platform = 'freebsd' + elif self._platform.startswith('openbsd'): + self._platform = 'openbsd' + elif self._platform.startswith('solaris') or self._platform == 'sunos5': + self._platform = 'solaris' + elif self._platform.startswith('mingw'): + self._platform = 'mingw' + elif self._platform.startswith('win'): + self._platform = 'msvc' + elif self._platform.startswith('bitrig'): + self._platform = 'bitrig' + elif self._platform.startswith('netbsd'): + self._platform = 'netbsd' + elif self._platform.startswith('aix'): + self._platform = 'aix' + elif self._platform.startswith('dragonfly'): + self._platform = 'dragonfly' + + @staticmethod + def known_platforms(): + return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5', + 'mingw', 'msvc', 'gnukfreebsd', 'bitrig', 'netbsd', 'aix', + 'dragonfly'] + + def platform(self): + return self._platform + + def is_linux(self): + return self._platform == 'linux' + + def is_mingw(self): + return self._platform == 'mingw' + + def is_msvc(self): + return self._platform == 'msvc' + + def msvc_needs_fs(self): + popen = subprocess.Popen(['cl', '/nologo', '/?'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = popen.communicate() + return b'/FS' in out + + def is_windows(self): + return self.is_mingw() or self.is_msvc() + + def is_solaris(self): + return self._platform == 'solaris' + + def is_aix(self): + return self._platform == 'aix' + + def uses_usr_local(self): + return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly') + + def supports_ppoll(self): + return self._platform in ('freebsd', 'linux', 'openbsd', 'bitrig', + 'dragonfly') + + def supports_ninja_browse(self): + return (not self.is_windows() + and not self.is_solaris() + and not self.is_aix()) + + def can_rebuild_in_place(self): + return not (self.is_windows() or self.is_aix()) + +class Bootstrap: + """API shim for ninja_syntax.Writer that instead runs the commands. + + Used to bootstrap Ninja from scratch. In --bootstrap mode this + class is used to execute all the commands to build an executable. + It also proxies all calls to an underlying ninja_syntax.Writer, to + behave like non-bootstrap mode. + """ + def __init__(self, writer, verbose=False): + self.writer = writer + self.verbose = verbose + # Map of variable name => expanded variable value. + self.vars = {} + # Map of rule name => dict of rule attributes. + self.rules = { + 'phony': {} + } + + def comment(self, text): + return self.writer.comment(text) + + def newline(self): + return self.writer.newline() + + def variable(self, key, val): + # In bootstrap mode, we have no ninja process to catch /showIncludes + # output. + self.vars[key] = self._expand(val).replace('/showIncludes', '') + return self.writer.variable(key, val) + + def rule(self, name, **kwargs): + self.rules[name] = kwargs + return self.writer.rule(name, **kwargs) + + def build(self, outputs, rule, inputs=None, **kwargs): + ruleattr = self.rules[rule] + cmd = ruleattr.get('command') + if cmd is None: # A phony rule, for example. + return + + # Implement just enough of Ninja variable expansion etc. to + # make the bootstrap build work. + local_vars = { + 'in': self._expand_paths(inputs), + 'out': self._expand_paths(outputs) + } + for key, val in kwargs.get('variables', []): + local_vars[key] = ' '.join(ninja_syntax.as_list(val)) + + self._run_command(self._expand(cmd, local_vars)) + + return self.writer.build(outputs, rule, inputs, **kwargs) + + def default(self, paths): + return self.writer.default(paths) + + def _expand_paths(self, paths): + """Expand $vars in an array of paths, e.g. from a 'build' block.""" + paths = ninja_syntax.as_list(paths) + return ' '.join(map(self._shell_escape, (map(self._expand, paths)))) + + def _expand(self, str, local_vars={}): + """Expand $vars in a string.""" + return ninja_syntax.expand(str, self.vars, local_vars) + + def _shell_escape(self, path): + """Quote paths containing spaces.""" + return '"%s"' % path if ' ' in path else path + + def _run_command(self, cmdline): + """Run a subcommand, quietly. Prints the full command on error.""" + try: + if self.verbose: + print(cmdline) + subprocess.check_call(cmdline, shell=True) + except subprocess.CalledProcessError: + print('when running: ', cmdline) + raise + + +parser = OptionParser() +profilers = ['gmon', 'pprof'] +parser.add_option('--bootstrap', action='http://github.com/store_true', + help='bootstrap a ninja binary from nothing') +parser.add_option('--verbose', action='http://github.com/store_true', + help='enable verbose build') +parser.add_option('--platform', + help='target platform (' + + '/'.join(Platform.known_platforms()) + ')', + choices=Platform.known_platforms()) +parser.add_option('--host', + help='host platform (' + + '/'.join(Platform.known_platforms()) + ')', + choices=Platform.known_platforms()) +parser.add_option('--debug', action='http://github.com/store_true', + help='enable debugging extras',) +parser.add_option('--profile', metavar='TYPE', + choices=profilers, + help='enable profiling (' + '/'.join(profilers) + ')',) +parser.add_option('--with-gtest', metavar='PATH', help='ignored') +parser.add_option('--with-python', metavar='EXE', + help='use EXE as the Python interpreter', + default=os.path.basename(sys.executable)) +parser.add_option('--force-pselect', action='http://github.com/store_true', + help='ppoll() is used by default where available, ' + 'but some platforms may need to use pselect instead',) +(options, args) = parser.parse_args() +if args: + print('ERROR: extra unparsed command-line arguments:', args) + sys.exit(1) + +platform = Platform(options.platform) +if options.host: + host = Platform(options.host) +else: + host = platform + +BUILD_FILENAME = 'build.ninja' +ninja_writer = ninja_syntax.Writer(open(BUILD_FILENAME, 'w')) +n = ninja_writer + +if options.bootstrap: + # Make the build directory. + try: + os.mkdir('build') + except OSError: + pass + # Wrap ninja_writer with the Bootstrapper, which also executes the + # commands. + print('bootstrapping ninja...') + n = Bootstrap(n, verbose=options.verbose) + +n.comment('This file is used to build ninja itself.') +n.comment('It is generated by ' + os.path.basename(__file__) + '.') +n.newline() + +n.variable('ninja_required_version', '1.3') +n.newline() + +n.comment('The arguments passed to configure.py, for rerunning it.') +configure_args = sys.argv[1:] +if '--bootstrap' in configure_args: + configure_args.remove('--bootstrap') +n.variable('configure_args', ' '.join(configure_args)) +env_keys = set(['CXX', 'AR', 'CFLAGS', 'LDFLAGS']) +configure_env = dict((k, os.environ[k]) for k in os.environ if k in env_keys) +if configure_env: + config_str = ' '.join([k + '=' + pipes.quote(configure_env[k]) + for k in configure_env]) + n.variable('configure_env', config_str + '$ ') +n.newline() + +CXX = configure_env.get('CXX', 'g++') +objext = '.o' +if platform.is_msvc(): + CXX = 'cl' + objext = '.obj' + +def src(filename): + return os.path.join('$root', 'src', filename) +def built(filename): + return os.path.join('$builddir', filename) +def doc(filename): + return os.path.join('$root', 'doc', filename) +def cc(name, **kwargs): + return n.build(built(name + objext), 'cxx', src(name + '.c'), **kwargs) +def cxx(name, **kwargs): + return n.build(built(name + objext), 'cxx', src(name + '.cc'), **kwargs) +def binary(name): + if platform.is_windows(): + exe = name + '.exe' + n.build(name, 'phony', exe) + return exe + return name + +root = sourcedir +if root == os.getcwd(): + # In the common case where we're building directly in the source + # tree, simplify all the paths to just be cwd-relative. + root = '.' +n.variable('root', root) +n.variable('builddir', 'build') +n.variable('cxx', CXX) +if platform.is_msvc(): + n.variable('ar', 'link') +else: + n.variable('ar', configure_env.get('AR', 'ar')) + +if platform.is_msvc(): + cflags = ['/showIncludes', + '/nologo', # Don't print startup banner. + '/Zi', # Create pdb with debug info. + '/W4', # Highest warning level. + '/WX', # Warnings as errors. + '/wd4530', '/wd4100', '/wd4706', '/wd4244', + '/wd4512', '/wd4800', '/wd4702', '/wd4819', + # Disable warnings about constant conditional expressions. + '/wd4127', + # Disable warnings about passing "this" during initialization. + '/wd4355', + # Disable warnings about ignored typedef in DbgHelp.h + '/wd4091', + '/GR-', # Disable RTTI. + # Disable size_t -> int truncation warning. + # We never have strings or arrays larger than 2**31. + '/wd4267', + '/DNOMINMAX', '/D_CRT_SECURE_NO_WARNINGS', + '/D_HAS_EXCEPTIONS=0', + '/DNINJA_PYTHON="%s"' % options.with_python] + if platform.msvc_needs_fs(): + cflags.append('/FS') + ldflags = ['/DEBUG', '/libpath:$builddir'] + if not options.debug: + cflags += ['/Ox', '/DNDEBUG', '/GL'] + ldflags += ['/LTCG', '/OPT:REF', '/OPT:ICF'] +else: + cflags = ['-g', '-Wall', '-Wextra', + '-Wno-deprecated', + '-Wno-missing-field-initializers', + '-Wno-unused-parameter', + '-fno-rtti', + '-fno-exceptions', + '-fvisibility=hidden', '-pipe', + '-DNINJA_PYTHON="%s"' % options.with_python] + if options.debug: + cflags += ['-D_GLIBCXX_DEBUG', '-D_GLIBCXX_DEBUG_PEDANTIC'] + cflags.remove('-fno-rtti') # Needed for above pedanticness. + else: + cflags += ['-O2', '-DNDEBUG'] + try: + proc = subprocess.Popen( + [CXX, '-fdiagnostics-color', '-c', '-x', 'c++', '/dev/null', + '-o', '/dev/null'], + stdout=open(os.devnull, 'wb'), stderr=subprocess.STDOUT) + if proc.wait() == 0: + cflags += ['-fdiagnostics-color'] + except: + pass + if platform.is_mingw(): + cflags += ['-D_WIN32_WINNT=0x0501'] + ldflags = ['-L$builddir'] + if platform.uses_usr_local(): + cflags.append('-I/usr/local/include') + ldflags.append('-L/usr/local/lib') + +libs = [] + +if platform.is_mingw(): + cflags.remove('-fvisibility=hidden'); + ldflags.append('-static') +elif platform.is_solaris(): + cflags.remove('-fvisibility=hidden') +elif platform.is_aix(): + cflags.remove('-fvisibility=hidden') +elif platform.is_msvc(): + pass +else: + if options.profile == 'gmon': + cflags.append('-pg') + ldflags.append('-pg') + elif options.profile == 'pprof': + cflags.append('-fno-omit-frame-pointer') + libs.extend(['-Wl,--no-as-needed', '-lprofiler']) + +if platform.supports_ppoll() and not options.force_pselect: + cflags.append('-DUSE_PPOLL') +if platform.supports_ninja_browse(): + cflags.append('-DNINJA_HAVE_BROWSE') + +# Search for generated headers relative to build dir. +cflags.append('-I.') + +def shell_escape(str): + """Escape str such that it's interpreted as a single argument by + the shell.""" + + # This isn't complete, but it's just enough to make NINJA_PYTHON work. + if platform.is_windows(): + return str + if '"' in str: + return "'%s'" % str.replace("'", "\\'") + return str + +if 'CFLAGS' in configure_env: + cflags.append(configure_env['CFLAGS']) +n.variable('cflags', ' '.join(shell_escape(flag) for flag in cflags)) +if 'LDFLAGS' in configure_env: + ldflags.append(configure_env['LDFLAGS']) +n.variable('ldflags', ' '.join(shell_escape(flag) for flag in ldflags)) +n.newline() + +if platform.is_msvc(): + n.rule('cxx', + command='$cxx $cflags -c $in /Fo$out', + description='CXX $out', + deps='msvc' # /showIncludes is included in $cflags. + ) +else: + n.rule('cxx', + command='$cxx -MMD -MT $out -MF $out.d $cflags -c $in -o $out', + depfile='$out.d', + deps='gcc', + description='CXX $out') +n.newline() + +if host.is_msvc(): + n.rule('ar', + command='lib /nologo /ltcg /out:$out $in', + description='LIB $out') +elif host.is_mingw(): + n.rule('ar', + command='cmd /c $ar cqs $out.tmp $in && move /Y $out.tmp $out', + description='AR $out') +else: + n.rule('ar', + command='rm -f $out && $ar crs $out $in', + description='AR $out') +n.newline() + +if platform.is_msvc(): + n.rule('link', + command='$cxx $in $libs /nologo /link $ldflags /out:$out', + description='LINK $out') +else: + n.rule('link', + command='$cxx $ldflags -o $out $in $libs', + description='LINK $out') +n.newline() + +objs = [] + +if platform.supports_ninja_browse(): + n.comment('browse_py.h is used to inline browse.py.') + n.rule('inline', + command='"%s"' % src('inline.sh') + ' $varname < $in > $out', + description='INLINE $out') + n.build(built('browse_py.h'), 'inline', src('browse.py'), + implicit=src('inline.sh'), + variables=[('varname', 'kBrowsePy')]) + n.newline() + + objs += cxx('browse', order_only=built('browse_py.h')) + n.newline() + +n.comment('the depfile parser and ninja lexers are generated using re2c.') +def has_re2c(): + try: + proc = subprocess.Popen(['re2c', '-V'], stdout=subprocess.PIPE) + return int(proc.communicate()[0], 10) >= 1103 + except OSError: + return False +if has_re2c(): + n.rule('re2c', + command='re2c -b -i --no-generation-date -o $out $in', + description='RE2C $out') + # Generate the .cc files in the source directory so we can check them in. + n.build(src('depfile_parser.cc'), 're2c', src('depfile_parser.in.cc')) + n.build(src('lexer.cc'), 're2c', src('lexer.in.cc')) +else: + print("warning: A compatible version of re2c (>= 0.11.3) was not found; " + "changes to src/*.in.cc will not affect your build.") +n.newline() + +n.comment('Core source files all build into ninja library.') +for name in ['build', + 'build_log', + 'clean', + 'clparser', + 'debug_flags', + 'depfile_parser', + 'deps_log', + 'disk_interface', + 'edit_distance', + 'eval_env', + 'graph', + 'graphviz', + 'lexer', + 'line_printer', + 'manifest_parser', + 'metrics', + 'state', + 'string_piece_util', + 'util', + 'version']: + objs += cxx(name) +if platform.is_windows(): + for name in ['subprocess-win32', + 'includes_normalize-win32', + 'msvc_helper-win32', + 'msvc_helper_main-win32']: + objs += cxx(name) + if platform.is_msvc(): + objs += cxx('minidump-win32') + objs += cc('getopt') +else: + objs += cxx('subprocess-posix') +if platform.is_aix(): + objs += cc('getopt') +if platform.is_msvc(): + ninja_lib = n.build(built('ninja.lib'), 'ar', objs) +else: + ninja_lib = n.build(built('libninja.a'), 'ar', objs) +n.newline() + +if platform.is_msvc(): + libs.append('ninja.lib') +else: + libs.append('-lninja') + +if platform.is_aix(): + libs.append('-lperfstat') + +all_targets = [] + +n.comment('Main executable is library plus main() function.') +objs = cxx('ninja') +ninja = n.build(binary('ninja'), 'link', objs, implicit=ninja_lib, + variables=[('libs', libs)]) +n.newline() +all_targets += ninja + +if options.bootstrap: + # We've built the ninja binary. Don't run any more commands + # through the bootstrap executor, but continue writing the + # build.ninja file. + n = ninja_writer + +n.comment('Tests all build into ninja_test executable.') + +objs = [] + +for name in ['build_log_test', + 'build_test', + 'clean_test', + 'clparser_test', + 'depfile_parser_test', + 'deps_log_test', + 'disk_interface_test', + 'edit_distance_test', + 'graph_test', + 'lexer_test', + 'manifest_parser_test', + 'ninja_test', + 'state_test', + 'string_piece_util_test', + 'subprocess_test', + 'test', + 'util_test']: + objs += cxx(name) +if platform.is_windows(): + for name in ['includes_normalize_test', 'msvc_helper_test']: + objs += cxx(name) + +ninja_test = n.build(binary('ninja_test'), 'link', objs, implicit=ninja_lib, + variables=[('libs', libs)]) +n.newline() +all_targets += ninja_test + + +n.comment('Ancillary executables.') + +for name in ['build_log_perftest', + 'canon_perftest', + 'depfile_parser_perftest', + 'hash_collision_bench', + 'manifest_parser_perftest', + 'clparser_perftest']: + objs = cxx(name) + all_targets += n.build(binary(name), 'link', objs, + implicit=ninja_lib, variables=[('libs', libs)]) + +n.newline() + +n.comment('Generate a graph using the "graph" tool.') +n.rule('gendot', + command='./ninja -t graph all > $out') +n.rule('gengraph', + command='dot -Tpng $in > $out') +dot = n.build(built('graph.dot'), 'gendot', ['ninja', 'build.ninja']) +n.build('graph.png', 'gengraph', dot) +n.newline() + +n.comment('Generate the manual using asciidoc.') +n.rule('asciidoc', + command='asciidoc -b docbook -d book -o $out $in', + description='ASCIIDOC $out') +n.rule('xsltproc', + command='xsltproc --nonet doc/docbook.xsl $in > $out', + description='XSLTPROC $out') +docbookxml = n.build(built('manual.xml'), 'asciidoc', doc('manual.asciidoc')) +manual = n.build(doc('manual.html'), 'xsltproc', docbookxml, + implicit=[doc('style.css'), doc('docbook.xsl')]) +n.build('manual', 'phony', + order_only=manual) +n.newline() + +n.rule('dblatex', + command='dblatex -q -o $out -p doc/dblatex.xsl $in', + description='DBLATEX $out') +n.build(doc('manual.pdf'), 'dblatex', docbookxml, + implicit=[doc('dblatex.xsl')]) + +n.comment('Generate Doxygen.') +n.rule('doxygen', + command='doxygen $in', + description='DOXYGEN $in') +n.variable('doxygen_mainpage_generator', + src('gen_doxygen_mainpage.sh')) +n.rule('doxygen_mainpage', + command='$doxygen_mainpage_generator $in > $out', + description='DOXYGEN_MAINPAGE $out') +mainpage = n.build(built('doxygen_mainpage'), 'doxygen_mainpage', + ['README', 'COPYING'], + implicit=['$doxygen_mainpage_generator']) +n.build('doxygen', 'doxygen', doc('doxygen.config'), + implicit=mainpage) +n.newline() + +if not host.is_mingw(): + n.comment('Regenerate build files if build script changes.') + n.rule('configure', + command='${configure_env}%s $root/configure.py $configure_args' % + options.with_python, + generator=True) + n.build('build.ninja', 'configure', + implicit=['$root/configure.py', + os.path.normpath('$root/misc/ninja_syntax.py')]) + n.newline() + +n.default(ninja) +n.newline() + +if host.is_linux(): + n.comment('Packaging') + n.rule('rpmbuild', + command="misc/packaging/rpmbuild.sh", + description='Building rpms..') + n.build('rpm', 'rpmbuild') + n.newline() + +n.build('all', 'phony', all_targets) + +n.close() +print('wrote %s.' % BUILD_FILENAME) + +if options.bootstrap: + print('bootstrap complete. rebuilding...') + + rebuild_args = [] + + if platform.can_rebuild_in_place(): + rebuild_args.append('./ninja') + else: + if platform.is_windows(): + bootstrap_exe = 'ninja.bootstrap.exe' + final_exe = 'ninja.exe' + else: + bootstrap_exe = './ninja.bootstrap' + final_exe = './ninja' + + if os.path.exists(bootstrap_exe): + os.unlink(bootstrap_exe) + os.rename(final_exe, bootstrap_exe) + + rebuild_args.append(bootstrap_exe) + + if options.verbose: + rebuild_args.append('-v') + + subprocess.check_call(rebuild_args) diff --git a/src/3rdparty/ninja/doc/README.md b/src/3rdparty/ninja/doc/README.md new file mode 100644 index 00000000000..6afe5d4672e --- /dev/null +++ b/src/3rdparty/ninja/doc/README.md @@ -0,0 +1,11 @@ +This directory contains the Ninja manual and support files used in +building it. Here's a brief overview of how it works. + +The source text, `manual.asciidoc`, is written in the AsciiDoc format. +AsciiDoc can generate HTML but it doesn't look great; instead, we use +AsciiDoc to generate the Docbook XML format and then provide our own +Docbook XSL tweaks to produce HTML from that. + +In theory using AsciiDoc and DocBook allows us to produce nice PDF +documentation etc. In reality it's not clear anyone wants that, but the +build rules are in place to generate it if you install dblatex. diff --git a/src/3rdparty/ninja/doc/dblatex.xsl b/src/3rdparty/ninja/doc/dblatex.xsl new file mode 100644 index 00000000000..c0da212708b --- /dev/null +++ b/src/3rdparty/ninja/doc/dblatex.xsl @@ -0,0 +1,7 @@ + + + + 0 + 0 + diff --git a/src/3rdparty/ninja/doc/docbook.xsl b/src/3rdparty/ninja/doc/docbook.xsl new file mode 100644 index 00000000000..19cc1263d59 --- /dev/null +++ b/src/3rdparty/ninja/doc/docbook.xsl @@ -0,0 +1,31 @@ + + +]> + + + + + + + + + + + book toc + + + 0 + + + ul + + + diff --git a/src/3rdparty/ninja/doc/doxygen.config b/src/3rdparty/ninja/doc/doxygen.config new file mode 100644 index 00000000000..d933021e2ba --- /dev/null +++ b/src/3rdparty/ninja/doc/doxygen.config @@ -0,0 +1,1250 @@ +# Doxyfile 1.4.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "Ninja" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +# PROJECT_NUMBER = "0" + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = "doc/doxygen/" + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +# Obsolet option. +#USE_WINDOWS_ENCODING = YES + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = src + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = src/ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +# Has become obsolete. +#DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +# BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is YES. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text " + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src \ + build/doxygen_mainpage + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = *.cc \ + *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = src + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = *.cpp \ + *.cc \ + *.h \ + *.hh \ + INSTALL DEPENDENCIES CHANGELOG LICENSE LGPL + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = src + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 2 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. +HTML_HEADER = + + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = doc/doxygen/html/Ninja.TAGFILE + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO +# UML_LOOK = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +# Obsolet option. +#MAX_DOT_GRAPH_WIDTH = 1280 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +# Obsolet option. +#MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. +# JW +# DOT_MULTI_TARGETS = NO +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +# JW SEARCHENGINE = NO +SEARCHENGINE = YES diff --git a/src/3rdparty/ninja/doc/manual.asciidoc b/src/3rdparty/ninja/doc/manual.asciidoc new file mode 100644 index 00000000000..17d607a3253 --- /dev/null +++ b/src/3rdparty/ninja/doc/manual.asciidoc @@ -0,0 +1,1006 @@ +The Ninja build system +====================== +v1.8.2, Sep 2017 + + +Introduction +------------ + +Ninja is yet another build system. It takes as input the +interdependencies of files (typically source code and output +executables) and orchestrates building them, _quickly_. + +Ninja joins a sea of other build systems. Its distinguishing goal is +to be fast. It is born from +http://neugierig.org/software/chromium/notes/2011/02/ninja.html[my +work on the Chromium browser project], which has over 30,000 source +files and whose other build systems (including one built from custom +non-recursive Makefiles) would take ten seconds to start building +after changing one file. Ninja is under a second. + +Philosophical overview +~~~~~~~~~~~~~~~~~~~~~~ + +Where other build systems are high-level languages, Ninja aims to be +an assembler. + +Build systems get slow when they need to make decisions. When you are +in a edit-compile cycle you want it to be as fast as possible -- you +want the build system to do the minimum work necessary to figure out +what needs to be built immediately. + +Ninja contains the barest functionality necessary to describe +arbitrary dependency graphs. Its lack of syntax makes it impossible +to express complex decisions. + +Instead, Ninja is intended to be used with a separate program +generating its input files. The generator program (like the +`./configure` found in autotools projects) can analyze system +dependencies and make as many decisions as possible up front so that +incremental builds stay fast. Going beyond autotools, even build-time +decisions like "which compiler flags should I use?" or "should I +build a debug or release-mode binary?" belong in the `.ninja` file +generator. + +Design goals +~~~~~~~~~~~~ + +Here are the design goals of Ninja: + +* very fast (i.e., instant) incremental builds, even for very large + projects. + +* very little policy about how code is built. Different projects and + higher-level build systems have different opinions about how code + should be built; for example, should built objects live alongside + the sources or should all build output go into a separate directory? + Is there a "package" rule that builds a distributable package of + the project? Sidestep these decisions by trying to allow either to + be implemented, rather than choosing, even if that results in + more verbosity. + +* get dependencies correct, and in particular situations that are + difficult to get right with Makefiles (e.g. outputs need an implicit + dependency on the command line used to generate them; to build C + source code you need to use gcc's `-M` flags for header + dependencies). + +* when convenience and speed are in conflict, prefer speed. + +Some explicit _non-goals_: + +* convenient syntax for writing build files by hand. _You should + generate your ninja files using another program_. This is how we + can sidestep many policy decisions. + +* built-in rules. _Out of the box, Ninja has no rules for + e.g. compiling C code._ + +* build-time customization of the build. _Options belong in + the program that generates the ninja files_. + +* build-time decision-making ability such as conditionals or search + paths. _Making decisions is slow._ + +To restate, Ninja is faster than other build systems because it is +painfully simple. You must tell Ninja exactly what to do when you +create your project's `.ninja` files. + +Comparison to Make +~~~~~~~~~~~~~~~~~~ + +Ninja is closest in spirit and functionality to Make, relying on +simple dependencies between file timestamps. + +But fundamentally, make has a lot of _features_: suffix rules, +functions, built-in rules that e.g. search for RCS files when building +source. Make's language was designed to be written by humans. Many +projects find make alone adequate for their build problems. + +In contrast, Ninja has almost no features; just those necessary to get +builds correct while punting most complexity to generation of the +ninja input files. Ninja by itself is unlikely to be useful for most +projects. + +Here are some of the features Ninja adds to Make. (These sorts of +features can often be implemented using more complicated Makefiles, +but they are not part of make itself.) + +* Ninja has special support for discovering extra dependencies at build + time, making it easy to get <> + correct for C/C++ code. + +* A build edge may have multiple outputs. + +* Outputs implicitly depend on the command line that was used to generate + them, which means that changing e.g. compilation flags will cause + the outputs to rebuild. + +* Output directories are always implicitly created before running the + command that relies on them. + +* Rules can provide shorter descriptions of the command being run, so + you can print e.g. `CC foo.o` instead of a long command line while + building. + +* Builds are always run in parallel, based by default on the number of + CPUs your system has. Underspecified build dependencies will result + in incorrect builds. + +* Command output is always buffered. This means commands running in + parallel don't interleave their output, and when a command fails we + can print its failure output next to the full command line that + produced the failure. + + +Using Ninja for your project +---------------------------- + +Ninja currently works on Unix-like systems and Windows. It's seen the +most testing on Linux (and has the best performance there) but it runs +fine on Mac OS X and FreeBSD. + +If your project is small, Ninja's speed impact is likely unnoticeable. +(However, even for small projects it sometimes turns out that Ninja's +limited syntax forces simpler build rules that result in faster +builds.) Another way to say this is that if you're happy with the +edit-compile cycle time of your project already then Ninja won't help. + +There are many other build systems that are more user-friendly or +featureful than Ninja itself. For some recommendations: the Ninja +author found http://gittup.org/tup/[the tup build system] influential +in Ninja's design, and thinks https://github.com/apenwarr/redo[redo]'s +design is quite clever. + +Ninja's benefit comes from using it in conjunction with a smarter +meta-build system. + +http://code.google.com/p/gyp/[gyp]:: The meta-build system used to +generate build files for Google Chrome and related projects (v8, +node.js). gyp can generate Ninja files for all platforms supported by +Chrome. See the +https://chromium.googlesource.com/chromium/src/+/master/docs/ninja_build.md[Chromium Ninja documentation for more details]. + +https://cmake.org/[CMake]:: A widely used meta-build system that +can generate Ninja files on Linux as of CMake version 2.8.8. Newer versions +of CMake support generating Ninja files on Windows and Mac OS X too. + +https://github.com/ninja-build/ninja/wiki/List-of-generators-producing-ninja-build-files[others]:: Ninja ought to fit perfectly into other meta-build software +like http://industriousone.com/premake[premake]. If you do this work, +please let us know! + +Running Ninja +~~~~~~~~~~~~~ + +Run `ninja`. By default, it looks for a file named `build.ninja` in +the current directory and builds all out-of-date targets. You can +specify which targets (files) to build as command line arguments. + +There is also a special syntax `target^` for specifying a target +as the first output of some rule containing the source you put in +the command line, if one exists. For example, if you specify target as +`foo.c^` then `foo.o` will get built (assuming you have those targets +in your build files). + +`ninja -h` prints help output. Many of Ninja's flags intentionally +match those of Make; e.g `ninja -C build -j 20` changes into the +`build` directory and runs 20 build commands in parallel. (Note that +Ninja defaults to running commands in parallel anyway, so typically +you don't need to pass `-j`.) + + +Environment variables +~~~~~~~~~~~~~~~~~~~~~ + +Ninja supports one environment variable to control its behavior: +`NINJA_STATUS`, the progress status printed before the rule being run. + +Several placeholders are available: + +`%s`:: The number of started edges. +`%t`:: The total number of edges that must be run to complete the build. +`%p`:: The percentage of started edges. +`%r`:: The number of currently running edges. +`%u`:: The number of remaining edges to start. +`%f`:: The number of finished edges. +`%o`:: Overall rate of finished edges per second +`%c`:: Current rate of finished edges per second (average over builds +specified by `-j` or its default) +`%e`:: Elapsed time in seconds. _(Available since Ninja 1.2.)_ +`%%`:: A plain `%` character. + +The default progress status is `"[%f/%t] "` (note the trailing space +to separate from the build rule). Another example of possible progress status +could be `"[%u/%r/%f] "`. + +Extra tools +~~~~~~~~~~~ + +The `-t` flag on the Ninja command line runs some tools that we have +found useful during Ninja's development. The current tools are: + +[horizontal] +`query`:: dump the inputs and outputs of a given target. + +`browse`:: browse the dependency graph in a web browser. Clicking a +file focuses the view on that file, showing inputs and outputs. This +feature requires a Python installation. By default port 8000 is used +and a web browser will be opened. This can be changed as follows: ++ +---- +ninja -t browse --port=8000 --no-browser mytarget +---- ++ +`graph`:: output a file in the syntax used by `graphviz`, a automatic +graph layout tool. Use it like: ++ +---- +ninja -t graph mytarget | dot -Tpng -ograph.png +---- ++ +In the Ninja source tree, `ninja graph.png` +generates an image for Ninja itself. If no target is given generate a +graph for all root targets. + +`targets`:: output a list of targets either by rule or by depth. If used +like +ninja -t targets rule _name_+ it prints the list of targets +using the given rule to be built. If no rule is given, it prints the source +files (the leaves of the graph). If used like ++ninja -t targets depth _digit_+ it +prints the list of targets in a depth-first manner starting by the root +targets (the ones with no outputs). Indentation is used to mark dependencies. +If the depth is zero it prints all targets. If no arguments are provided ++ninja -t targets depth 1+ is assumed. In this mode targets may be listed +several times. If used like this +ninja -t targets all+ it +prints all the targets available without indentation and it is faster +than the _depth_ mode. + +`commands`:: given a list of targets, print a list of commands which, if +executed in order, may be used to rebuild those targets, assuming that all +output files are out of date. + +`clean`:: remove built files. By default it removes all built files +except for those created by the generator. Adding the `-g` flag also +removes built files created by the generator (see <>). Additional arguments are +targets, which removes the given targets and recursively all files +built for them. ++ +If used like +ninja -t clean -r _rules_+ it removes all files built using +the given rules. ++ +Files created but not referenced in the graph are not removed. This +tool takes in account the +-v+ and the +-n+ options (note that +-n+ +implies +-v+). + +`compdb`:: given a list of rules, each of which is expected to be a +C family language compiler rule whose first input is the name of the +source file, prints on standard output a compilation database in the +http://clang.llvm.org/docs/JSONCompilationDatabase.html[JSON format] expected +by the Clang tooling interface. +_Available since Ninja 1.2._ + +`deps`:: show all dependencies stored in the `.ninja_deps` file. When given a +target, show just the target's dependencies. _Available since Ninja 1.4._ + +`recompact`:: recompact the `.ninja_deps` file. _Available since Ninja 1.4._ + + +Writing your own Ninja files +---------------------------- + +The remainder of this manual is only useful if you are constructing +Ninja files yourself: for example, if you're writing a meta-build +system or supporting a new language. + +Conceptual overview +~~~~~~~~~~~~~~~~~~~ + +Ninja evaluates a graph of dependencies between files, and runs +whichever commands are necessary to make your build target up to date +as determined by file modification times. If you are familiar with +Make, Ninja is very similar. + +A build file (default name: `build.ninja`) provides a list of _rules_ +-- short names for longer commands, like how to run the compiler -- +along with a list of _build_ statements saying how to build files +using the rules -- which rule to apply to which inputs to produce +which outputs. + +Conceptually, `build` statements describe the dependency graph of your +project, while `rule` statements describe how to generate the files +along a given edge of the graph. + +Syntax example +~~~~~~~~~~~~~~ + +Here's a basic `.ninja` file that demonstrates most of the syntax. +It will be used as an example for the following sections. + +--------------------------------- +cflags = -Wall + +rule cc + command = gcc $cflags -c $in -o $out + +build foo.o: cc foo.c +--------------------------------- + +Variables +~~~~~~~~~ +Despite the non-goal of being convenient to write by hand, to keep +build files readable (debuggable), Ninja supports declaring shorter +reusable names for strings. A declaration like the following + +---------------- +cflags = -g +---------------- + +can be used on the right side of an equals sign, dereferencing it with +a dollar sign, like this: + +---------------- +rule cc + command = gcc $cflags -c $in -o $out +---------------- + +Variables can also be referenced using curly braces like `${in}`. + +Variables might better be called "bindings", in that a given variable +cannot be changed, only shadowed. There is more on how shadowing works +later in this document. + +Rules +~~~~~ + +Rules declare a short name for a command line. They begin with a line +consisting of the `rule` keyword and a name for the rule. Then +follows an indented set of `variable = value` lines. + +The basic example above declares a new rule named `cc`, along with the +command to run. In the context of a rule, the `command` variable +defines the command to run, `$in` expands to the list of +input files (`foo.c`), and `$out` to the output files (`foo.o`) for the +command. A full list of special variables is provided in +<>. + +Build statements +~~~~~~~~~~~~~~~~ + +Build statements declare a relationship between input and output +files. They begin with the `build` keyword, and have the format ++build _outputs_: _rulename_ _inputs_+. Such a declaration says that +all of the output files are derived from the input files. When the +output files are missing or when the inputs change, Ninja will run the +rule to regenerate the outputs. + +The basic example above describes how to build `foo.o`, using the `cc` +rule. + +In the scope of a `build` block (including in the evaluation of its +associated `rule`), the variable `$in` is the list of inputs and the +variable `$out` is the list of outputs. + +A build statement may be followed by an indented set of `key = value` +pairs, much like a rule. These variables will shadow any variables +when evaluating the variables in the command. For example: + +---------------- +cflags = -Wall -Werror +rule cc + command = gcc $cflags -c $in -o $out + +# If left unspecified, builds get the outer $cflags. +build foo.o: cc foo.c + +# But you can shadow variables like cflags for a particular build. +build special.o: cc special.c + cflags = -Wall + +# The variable was only shadowed for the scope of special.o; +# Subsequent build lines get the outer (original) cflags. +build bar.o: cc bar.c + +---------------- + +For more discussion of how scoping works, consult <>. + +If you need more complicated information passed from the build +statement to the rule (for example, if the rule needs "the file +extension of the first input"), pass that through as an extra +variable, like how `cflags` is passed above. + +If the top-level Ninja file is specified as an output of any build +statement and it is out of date, Ninja will rebuild and reload it +before building the targets requested by the user. + +Generating Ninja files from code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`misc/ninja_syntax.py` in the Ninja distribution is a tiny Python +module to facilitate generating Ninja files. It allows you to make +Python calls like `ninja.rule(name='foo', command='bar', +depfile='$out.d')` and it will generate the appropriate syntax. Feel +free to just inline it into your project's build system if it's +useful. + + +More details +------------ + +The `phony` rule +~~~~~~~~~~~~~~~~ + +The special rule name `phony` can be used to create aliases for other +targets. For example: + +---------------- +build foo: phony some/file/in/a/faraway/subdir/foo +---------------- + +This makes `ninja foo` build the longer path. Semantically, the +`phony` rule is equivalent to a plain rule where the `command` does +nothing, but phony rules are handled specially in that they aren't +printed when run, logged (see below), nor do they contribute to the +command count printed as part of the build process. + +`phony` can also be used to create dummy targets for files which +may not exist at build time. If a phony build statement is written +without any dependencies, the target will be considered out of date if +it does not exist. Without a phony build statement, Ninja will report +an error if the file does not exist and is required by the build. + + +Default target statements +~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, if no targets are specified on the command line, Ninja +will build every output that is not named as an input elsewhere. +You can override this behavior using a default target statement. +A default target statement causes Ninja to build only a given subset +of output files if none are specified on the command line. + +Default target statements begin with the `default` keyword, and have +the format +default _targets_+. A default target statement must appear +after the build statement that declares the target as an output file. +They are cumulative, so multiple statements may be used to extend +the list of default targets. For example: + +---------------- +default foo bar +default baz +---------------- + +This causes Ninja to build the `foo`, `bar` and `baz` targets by +default. + + +[[ref_log]] +The Ninja log +~~~~~~~~~~~~~ + +For each built file, Ninja keeps a log of the command used to build +it. Using this log Ninja can know when an existing output was built +with a different command line than the build files specify (i.e., the +command line changed) and knows to rebuild the file. + +The log file is kept in the build root in a file called `.ninja_log`. +If you provide a variable named `builddir` in the outermost scope, +`.ninja_log` will be kept in that directory instead. + + +[[ref_versioning]] +Version compatibility +~~~~~~~~~~~~~~~~~~~~~ + +_Available since Ninja 1.2._ + +Ninja version labels follow the standard major.minor.patch format, +where the major version is increased on backwards-incompatible +syntax/behavioral changes and the minor version is increased on new +behaviors. Your `build.ninja` may declare a variable named +`ninja_required_version` that asserts the minimum Ninja version +required to use the generated file. For example, + +----- +ninja_required_version = 1.1 +----- + +declares that the build file relies on some feature that was +introduced in Ninja 1.1 (perhaps the `pool` syntax), and that +Ninja 1.1 or greater must be used to build. Unlike other Ninja +variables, this version requirement is checked immediately when +the variable is encountered in parsing, so it's best to put it +at the top of the build file. + +Ninja always warns if the major versions of Ninja and the +`ninja_required_version` don't match; a major version change hasn't +come up yet so it's difficult to predict what behavior might be +required. + +[[ref_headers]] +C/C++ header dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To get C/C++ header dependencies (or any other build dependency that +works in a similar way) correct Ninja has some extra functionality. + +The problem with headers is that the full list of files that a given +source file depends on can only be discovered by the compiler: +different preprocessor defines and include paths cause different files +to be used. Some compilers can emit this information while building, +and Ninja can use that to get its dependencies perfect. + +Consider: if the file has never been compiled, it must be built anyway, +generating the header dependencies as a side effect. If any file is +later modified (even in a way that changes which headers it depends +on) the modification will cause a rebuild as well, keeping the +dependencies up to date. + +When loading these special dependencies, Ninja implicitly adds extra +build edges such that it is not an error if the listed dependency is +missing. This allows you to delete a header file and rebuild without +the build aborting due to a missing input. + +depfile +^^^^^^^ + +`gcc` (and other compilers like `clang`) support emitting dependency +information in the syntax of a Makefile. (Any command that can write +dependencies in this form can be used, not just `gcc`.) + +To bring this information into Ninja requires cooperation. On the +Ninja side, the `depfile` attribute on the `build` must point to a +path where this data is written. (Ninja only supports the limited +subset of the Makefile syntax emitted by compilers.) Then the command +must know to write dependencies into the `depfile` path. +Use it like in the following example: + +---- +rule cc + depfile = $out.d + command = gcc -MMD -MF $out.d [other gcc flags here] +---- + +The `-MMD` flag to `gcc` tells it to output header dependencies, and +the `-MF` flag tells it where to write them. + +deps +^^^^ + +_(Available since Ninja 1.3.)_ + +It turns out that for large projects (and particularly on Windows, +where the file system is slow) loading these dependency files on +startup is slow. + +Ninja 1.3 can instead process dependencies just after they're generated +and save a compacted form of the same information in a Ninja-internal +database. + +Ninja supports this processing in two forms. + +1. `deps = gcc` specifies that the tool outputs `gcc`-style dependencies + in the form of Makefiles. Adding this to the above example will + cause Ninja to process the `depfile` immediately after the + compilation finishes, then delete the `.d` file (which is only used + as a temporary). + +2. `deps = msvc` specifies that the tool outputs header dependencies + in the form produced by Visual Studio's compiler's + http://msdn.microsoft.com/en-us/library/hdkef6tk(v=vs.90).aspx[`/showIncludes` + flag]. Briefly, this means the tool outputs specially-formatted lines + to its stdout. Ninja then filters these lines from the displayed + output. No `depfile` attribute is necessary, but the localized string + in front of the the header file path. For instance + `msvc_deps_prefix = Note: including file: ` + for a English Visual Studio (the default). Should be globally defined. ++ +---- +msvc_deps_prefix = Note: including file: +rule cc + deps = msvc + command = cl /showIncludes -c $in /Fo$out +---- + +If the include directory directives are using absolute paths, your depfile +may result in a mixture of relative and absolute paths. Paths used by other +build rules need to match exactly. Therefore, it is recommended to use +relative paths in these cases. + +[[ref_pool]] +Pools +~~~~~ + +_Available since Ninja 1.1._ + +Pools allow you to allocate one or more rules or edges a finite number +of concurrent jobs which is more tightly restricted than the default +parallelism. + +This can be useful, for example, to restrict a particular expensive rule +(like link steps for huge executables), or to restrict particular build +statements which you know perform poorly when run concurrently. + +Each pool has a `depth` variable which is specified in the build file. +The pool is then referred to with the `pool` variable on either a rule +or a build statement. + +No matter what pools you specify, ninja will never run more concurrent jobs +than the default parallelism, or the number of jobs specified on the command +line (with `-j`). + +---------------- +# No more than 4 links at a time. +pool link_pool + depth = 4 + +# No more than 1 heavy object at a time. +pool heavy_object_pool + depth = 1 + +rule link + ... + pool = link_pool + +rule cc + ... + +# The link_pool is used here. Only 4 links will run concurrently. +build foo.exe: link input.obj + +# A build statement can be exempted from its rule's pool by setting an +# empty pool. This effectively puts the build statement back into the default +# pool, which has infinite depth. +build other.exe: link input.obj + pool = + +# A build statement can specify a pool directly. +# Only one of these builds will run at a time. +build heavy_object1.obj: cc heavy_obj1.cc + pool = heavy_object_pool +build heavy_object2.obj: cc heavy_obj2.cc + pool = heavy_object_pool + +---------------- + +The `console` pool +^^^^^^^^^^^^^^^^^^ + +_Available since Ninja 1.5._ + +There exists a pre-defined pool named `console` with a depth of 1. It has +the special property that any task in the pool has direct access to the +standard input, output and error streams provided to Ninja, which are +normally connected to the user's console (hence the name) but could be +redirected. This can be useful for interactive tasks or long-running tasks +which produce status updates on the console (such as test suites). + +While a task in the `console` pool is running, Ninja's regular output (such +as progress status and output from concurrent tasks) is buffered until +it completes. + +Ninja file reference +-------------------- + +A file is a series of declarations. A declaration can be one of: + +1. A rule declaration, which begins with +rule _rulename_+, and + then has a series of indented lines defining variables. + +2. A build edge, which looks like +build _output1_ _output2_: + _rulename_ _input1_ _input2_+. + + Implicit dependencies may be tacked on the end with +| + _dependency1_ _dependency2_+. + + Order-only dependencies may be tacked on the end with +|| + _dependency1_ _dependency2_+. (See <>.) ++ +Implicit outputs _(available since Ninja 1.7)_ may be added before +the `:` with +| _output1_ _output2_+ and do not appear in `$out`. +(See <>.) + +3. Variable declarations, which look like +_variable_ = _value_+. + +4. Default target statements, which look like +default _target1_ _target2_+. + +5. References to more files, which look like +subninja _path_+ or + +include _path_+. The difference between these is explained below + <>. + +6. A pool declaration, which looks like +pool _poolname_+. Pools are explained + <>. + +Lexical syntax +~~~~~~~~~~~~~~ + +Ninja is mostly encoding agnostic, as long as the bytes Ninja cares +about (like slashes in paths) are ASCII. This means e.g. UTF-8 or +ISO-8859-1 input files ought to work. + +Comments begin with `#` and extend to the end of the line. + +Newlines are significant. Statements like `build foo bar` are a set +of space-separated tokens that end at the newline. Newlines and +spaces within a token must be escaped. + +There is only one escape character, `$`, and it has the following +behaviors: + +`$` followed by a newline:: escape the newline (continue the current line +across a line break). + +`$` followed by text:: a variable reference. + +`${varname}`:: alternate syntax for `$varname`. + +`$` followed by space:: a space. (This is only necessary in lists of +paths, where a space would otherwise separate filenames. See below.) + +`$:` :: a colon. (This is only necessary in `build` lines, where a colon +would otherwise terminate the list of outputs.) + +`$$`:: a literal `$`. + +A `build` or `default` statement is first parsed as a space-separated +list of filenames and then each name is expanded. This means that +spaces within a variable will result in spaces in the expanded +filename. + +---- +spaced = foo bar +build $spaced/baz other$ file: ... +# The above build line has two outputs: "foo bar/baz" and "other file". +---- + +In a `name = value` statement, whitespace at the beginning of a value +is always stripped. Whitespace at the beginning of a line after a +line continuation is also stripped. + +---- +two_words_with_one_space = foo $ + bar +one_word_with_no_space = foo$ + bar +---- + +Other whitespace is only significant if it's at the beginning of a +line. If a line is indented more than the previous one, it's +considered part of its parent's scope; if it is indented less than the +previous one, it closes the previous scope. + +[[ref_toplevel]] +Top-level variables +~~~~~~~~~~~~~~~~~~~ + +Two variables are significant when declared in the outermost file scope. + +`builddir`:: a directory for some Ninja output files. See <>. (You can also store other build output + in this directory.) + +`ninja_required_version`:: the minimum version of Ninja required to process + the build correctly. See <>. + + +[[ref_rule]] +Rule variables +~~~~~~~~~~~~~~ + +A `rule` block contains a list of `key = value` declarations that +affect the processing of the rule. Here is a full list of special +keys. + +`command` (_required_):: the command line to run. Each `rule` may + have only one `command` declaration. See <> for more details on quoting and executing multiple commands. + +`depfile`:: path to an optional `Makefile` that contains extra + _implicit dependencies_ (see <>). This is explicitly to support C/C++ header + dependencies; see <>. + +`deps`:: _(Available since Ninja 1.3.)_ if present, must be one of + `gcc` or `msvc` to specify special dependency processing. See + <>. The generated database is + stored as `.ninja_deps` in the `builddir`, see <>. + +`msvc_deps_prefix`:: _(Available since Ninja 1.5.)_ defines the string + which should be stripped from msvc's /showIncludes output. Only + needed when `deps = msvc` and no English Visual Studio version is used. + +`description`:: a short description of the command, used to pretty-print + the command as it's running. The `-v` flag controls whether to print + the full command or its description; if a command fails, the full command + line will always be printed before the command's output. + +`generator`:: if present, specifies that this rule is used to + re-invoke the generator program. Files built using `generator` + rules are treated specially in two ways: firstly, they will not be + rebuilt if the command line changes; and secondly, they are not + cleaned by default. + +`in`:: the space-separated list of files provided as inputs to the build line + referencing this `rule`, shell-quoted if it appears in commands. (`$in` is + provided solely for convenience; if you need some subset or variant of this + list of files, just construct a new variable with that list and use + that instead.) + +`in_newline`:: the same as `$in` except that multiple inputs are + separated by newlines rather than spaces. (For use with + `$rspfile_content`; this works around a bug in the MSVC linker where + it uses a fixed-size buffer for processing input.) + +`out`:: the space-separated list of files provided as outputs to the build line + referencing this `rule`, shell-quoted if it appears in commands. + +`restat`:: if present, causes Ninja to re-stat the command's outputs + after execution of the command. Each output whose modification time + the command did not change will be treated as though it had never + needed to be built. This may cause the output's reverse + dependencies to be removed from the list of pending build actions. + +`rspfile`, `rspfile_content`:: if present (both), Ninja will use a + response file for the given command, i.e. write the selected string + (`rspfile_content`) to the given file (`rspfile`) before calling the + command and delete the file after successful execution of the + command. ++ +This is particularly useful on Windows OS, where the maximal length of +a command line is limited and response files must be used instead. ++ +Use it like in the following example: ++ +---- +rule link + command = link.exe /OUT$out [usual link flags here] @$out.rsp + rspfile = $out.rsp + rspfile_content = $in + +build myapp.exe: link a.obj b.obj [possibly many other .obj files] +---- + +[[ref_rule_command]] +Interpretation of the `command` variable +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Fundamentally, command lines behave differently on Unixes and Windows. + +On Unixes, commands are arrays of arguments. The Ninja `command` +variable is passed directly to `sh -c`, which is then responsible for +interpreting that string into an argv array. Therefore the quoting +rules are those of the shell, and you can use all the normal shell +operators, like `&&` to chain multiple commands, or `VAR=value cmd` to +set environment variables. + +On Windows, commands are strings, so Ninja passes the `command` string +directly to `CreateProcess`. (In the common case of simply executing +a compiler this means there is less overhead.) Consequently the +quoting rules are deterimined by the called program, which on Windows +are usually provided by the C library. If you need shell +interpretation of the command (such as the use of `&&` to chain +multiple commands), make the command execute the Windows shell by +prefixing the command with `cmd /c`. + +[[ref_outputs]] +Build outputs +~~~~~~~~~~~~~ + +There are two types of build outputs which are subtly different. + +1. _Explicit outputs_, as listed in a build line. These are + available as the `$out` variable in the rule. ++ +This is the standard form of output to be used for e.g. the +object file of a compile command. + +2. _Implicit outputs_, as listed in a build line with the syntax +| + _out1_ _out2_+ + before the `:` of a build line _(available since + Ninja 1.7)_. The semantics are identical to explicit outputs, + the only difference is that implicit outputs don't show up in the + `$out` variable. ++ +This is for expressing outputs that don't show up on the +command line of the command. + +[[ref_dependencies]] +Build dependencies +~~~~~~~~~~~~~~~~~~ + +There are three types of build dependencies which are subtly different. + +1. _Explicit dependencies_, as listed in a build line. These are + available as the `$in` variable in the rule. Changes in these files + cause the output to be rebuilt; if these file are missing and + Ninja doesn't know how to build them, the build is aborted. ++ +This is the standard form of dependency to be used e.g. for the +source file of a compile command. + +2. _Implicit dependencies_, either as picked up from + a `depfile` attribute on a rule or from the syntax +| _dep1_ + _dep2_+ on the end of a build line. The semantics are identical to + explicit dependencies, the only difference is that implicit dependencies + don't show up in the `$in` variable. ++ +This is for expressing dependencies that don't show up on the +command line of the command; for example, for a rule that runs a +script, the script itself should be an implicit dependency, as +changes to the script should cause the output to rebuild. ++ +Note that dependencies as loaded through depfiles have slightly different +semantics, as described in the <>. + +3. _Order-only dependencies_, expressed with the syntax +|| _dep1_ + _dep2_+ on the end of a build line. When these are out of date, the + output is not rebuilt until they are built, but changes in order-only + dependencies alone do not cause the output to be rebuilt. ++ +Order-only dependencies can be useful for bootstrapping dependencies +that are only discovered during build time: for example, to generate a +header file before starting a subsequent compilation step. (Once the +header is used in compilation, a generated dependency file will then +express the implicit dependency.) + +File paths are compared as is, which means that an absolute path and a +relative path, pointing to the same file, are considered different by Ninja. + +Variable expansion +~~~~~~~~~~~~~~~~~~ + +Variables are expanded in paths (in a `build` or `default` statement) +and on the right side of a `name = value` statement. + +When a `name = value` statement is evaluated, its right-hand side is +expanded immediately (according to the below scoping rules), and +from then on `$name` expands to the static string as the result of the +expansion. It is never the case that you'll need to "double-escape" a +value to prevent it from getting expanded twice. + +All variables are expanded immediately as they're encountered in parsing, +with one important exception: variables in `rule` blocks are expanded +when the rule is _used_, not when it is declared. In the following +example, the `demo` rule prints "this is a demo of bar". + +---- +rule demo + command = echo "this is a demo of $foo" + +build out: demo + foo = bar +---- + +[[ref_scope]] +Evaluation and scoping +~~~~~~~~~~~~~~~~~~~~~~ + +Top-level variable declarations are scoped to the file they occur in. + +Rule declarations are also scoped to the file they occur in. +_(Available since Ninja 1.6)_ + +The `subninja` keyword, used to include another `.ninja` file, +introduces a new scope. The included `subninja` file may use the +variables and rules from the parent file, and shadow their values for the file's +scope, but it won't affect values of the variables in the parent. + +To include another `.ninja` file in the current scope, much like a C +`#include` statement, use `include` instead of `subninja`. + +Variable declarations indented in a `build` block are scoped to the +`build` block. The full lookup order for a variable expanded in a +`build` block (or the `rule` is uses) is: + +1. Special built-in variables (`$in`, `$out`). + +2. Build-level variables from the `build` block. + +3. Rule-level variables from the `rule` block (i.e. `$command`). + (Note from the above discussion on expansion that these are + expanded "late", and may make use of in-scope bindings like `$in`.) + +4. File-level variables from the file that the `build` line was in. + +5. Variables from the file that included that file using the + `subninja` keyword. diff --git a/src/3rdparty/ninja/doc/style.css b/src/3rdparty/ninja/doc/style.css new file mode 100644 index 00000000000..9976c03ac36 --- /dev/null +++ b/src/3rdparty/ninja/doc/style.css @@ -0,0 +1,29 @@ +body { + margin: 5ex 10ex; + max-width: 80ex; + line-height: 1.5; + font-family: sans-serif; +} +h1, h2, h3 { + font-weight: normal; +} +pre, code { + font-family: x, monospace; +} +pre { + padding: 1ex; + background: #eee; + border: solid 1px #ddd; + min-width: 0; + font-size: 90%; +} +code { + color: #007; +} +div.chapter { + margin-top: 4em; + border-top: solid 2px black; +} +p { + margin-top: 0; +} diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_build b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_build new file mode 100644 index 00000000000..c795b054e5a --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_build @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_default b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_default new file mode 100644 index 00000000000..331d858ce9b --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_default @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_include b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_include new file mode 100644 index 00000000000..2996fba3563 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_include @@ -0,0 +1 @@ +include \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_pool b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_pool new file mode 100644 index 00000000000..e783591ae77 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_pool @@ -0,0 +1 @@ +pool \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_rule b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_rule new file mode 100644 index 00000000000..841e840f873 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_rule @@ -0,0 +1 @@ +rule \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_subninja b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_subninja new file mode 100644 index 00000000000..c4fe0c78f16 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/kw_subninja @@ -0,0 +1 @@ +subninja \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_a b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_a new file mode 100644 index 00000000000..2e65efe2a14 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_a @@ -0,0 +1 @@ +a \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_b b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_b new file mode 100644 index 00000000000..63d8dbd40c2 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_b @@ -0,0 +1 @@ +b \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_colon b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_colon new file mode 100644 index 00000000000..22ded55aa2c --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_colon @@ -0,0 +1 @@ +: \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_cont b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_cont new file mode 100644 index 00000000000..857f13ad1bc --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_cont @@ -0,0 +1 @@ +$ diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_dollar b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_dollar new file mode 100644 index 00000000000..6f4f765ed69 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_dollar @@ -0,0 +1 @@ +$ \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_eq b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_eq new file mode 100644 index 00000000000..851c75cc5e7 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_eq @@ -0,0 +1 @@ += \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_indent b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_indent new file mode 100644 index 00000000000..136d06384a4 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_indent @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipe b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipe new file mode 100644 index 00000000000..a3871d45082 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipe @@ -0,0 +1 @@ +| \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipepipe b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipepipe new file mode 100644 index 00000000000..27cc728d691 --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_pipepipe @@ -0,0 +1 @@ +|| \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_space b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_space new file mode 100644 index 00000000000..0519ecba6ea --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz-tokens/misc_space @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/3rdparty/ninja/misc/afl-fuzz/build.ninja b/src/3rdparty/ninja/misc/afl-fuzz/build.ninja new file mode 100644 index 00000000000..52cd2f151bb --- /dev/null +++ b/src/3rdparty/ninja/misc/afl-fuzz/build.ninja @@ -0,0 +1,5 @@ +rule b + command = clang -MMD -MF $out.d -o $out -c $in + description = building $out + +build a.o: b a.c diff --git a/src/3rdparty/ninja/misc/bash-completion b/src/3rdparty/ninja/misc/bash-completion new file mode 100644 index 00000000000..e604cd438c0 --- /dev/null +++ b/src/3rdparty/ninja/misc/bash-completion @@ -0,0 +1,57 @@ +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Add the following to your .bashrc to tab-complete ninja targets +# . path/to/ninja/misc/bash-completion + +_ninja_target() { + local cur prev targets dir line targets_command OPTIND + + # When available, use bash_completion to: + # 1) Complete words when the cursor is in the middle of the word + # 2) Complete paths with files or directories, as appropriate + if _get_comp_words_by_ref cur prev &>/dev/null ; then + case $prev in + -f) + _filedir + return 0 + ;; + -C) + _filedir -d + return 0 + ;; + esac + else + cur="${COMP_WORDS[COMP_CWORD]}" + fi + + if [[ "$cur" == "--"* ]]; then + # there is currently only one argument that takes -- + COMPREPLY=($(compgen -P '--' -W 'version' -- "${cur:2}")) + else + dir="." + line=$(echo ${COMP_LINE} | cut -d" " -f 2-) + # filter out all non relevant arguments but keep C for dirs + while getopts :C:f:j:l:k:nvd:t: opt $line; do + case $opt in + # eval for tilde expansion + C) eval dir="$OPTARG" ;; + esac + done; + targets_command="eval ninja -C \"${dir}\" -t targets all 2>/dev/null | cut -d: -f1" + COMPREPLY=($(compgen -W '`${targets_command}`' -- "$cur")) + fi + return +} +complete -F _ninja_target ninja diff --git a/src/3rdparty/ninja/misc/inherited-fds.ninja b/src/3rdparty/ninja/misc/inherited-fds.ninja new file mode 100644 index 00000000000..671155eb0b3 --- /dev/null +++ b/src/3rdparty/ninja/misc/inherited-fds.ninja @@ -0,0 +1,23 @@ +# This build file prints out a list of open file descriptors in +# Ninja subprocesses, to help verify we don't accidentally leak +# any. + +# Because one fd leak was in the code managing multiple subprocesses, +# this test brings up multiple subprocesses and then dumps the fd +# table of the last one. + +# Use like: ./ninja -f misc/inherited-fds.ninja + +rule sleep + command = sleep 10000 + +rule dump + command = sleep 1; ls -l /proc/self/fd; exit 1 + +build all: phony a b c d e + +build a: sleep +build b: sleep +build c: sleep +build d: sleep +build e: dump diff --git a/src/3rdparty/ninja/misc/long-slow-build.ninja b/src/3rdparty/ninja/misc/long-slow-build.ninja new file mode 100644 index 00000000000..46af6bafbe7 --- /dev/null +++ b/src/3rdparty/ninja/misc/long-slow-build.ninja @@ -0,0 +1,38 @@ +# An input file for running a "slow" build. +# Use like: ninja -f misc/long-slow-build.ninja all + +rule sleep + command = sleep 1 + description = SLEEP $out + +build 0: sleep README +build 1: sleep README +build 2: sleep README +build 3: sleep README +build 4: sleep README +build 5: sleep README +build 6: sleep README +build 7: sleep README +build 8: sleep README +build 9: sleep README +build 10: sleep 0 +build 11: sleep 1 +build 12: sleep 2 +build 13: sleep 3 +build 14: sleep 4 +build 15: sleep 5 +build 16: sleep 6 +build 17: sleep 7 +build 18: sleep 8 +build 19: sleep 9 +build 20: sleep 10 +build 21: sleep 11 +build 22: sleep 12 +build 23: sleep 13 +build 24: sleep 14 +build 25: sleep 15 +build 26: sleep 16 +build 27: sleep 17 +build 28: sleep 18 +build 29: sleep 19 +build all: phony 20 21 22 23 24 25 26 27 28 29 diff --git a/src/3rdparty/ninja/misc/measure.py b/src/3rdparty/ninja/misc/measure.py new file mode 100755 index 00000000000..8ce95e696b2 --- /dev/null +++ b/src/3rdparty/ninja/misc/measure.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""measure the runtime of a command by repeatedly running it. +""" + +from __future__ import print_function + +import time +import subprocess +import sys + +devnull = open('/dev/null', 'w') + +def run(cmd, repeat=10): + print('sampling:', end=' ') + sys.stdout.flush() + + samples = [] + for _ in range(repeat): + start = time.time() + subprocess.call(cmd, stdout=devnull, stderr=devnull) + end = time.time() + dt = (end - start) * 1000 + print('%dms' % int(dt), end=' ') + sys.stdout.flush() + samples.append(dt) + print() + + # We're interested in the 'pure' runtime of the code, which is + # conceptually the smallest time we'd see if we ran it enough times + # such that it got the perfect time slices / disk cache hits. + best = min(samples) + # Also print how varied the outputs were in an attempt to make it + # more obvious if something has gone terribly wrong. + err = sum(s - best for s in samples) / float(len(samples)) + print('estimate: %dms (mean err %.1fms)' % (best, err)) + +if __name__ == '__main__': + if len(sys.argv) < 2: + print('usage: measure.py command args...') + sys.exit(1) + run(cmd=sys.argv[1:]) diff --git a/src/3rdparty/ninja/misc/ninja-mode.el b/src/3rdparty/ninja/misc/ninja-mode.el new file mode 100644 index 00000000000..639e5375bb0 --- /dev/null +++ b/src/3rdparty/ninja/misc/ninja-mode.el @@ -0,0 +1,85 @@ +;;; ninja-mode.el --- Major mode for editing .ninja files -*- lexical-binding: t -*- + +;; Package-Requires: ((emacs "24")) + +;; Copyright 2011 Google Inc. All Rights Reserved. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +;;; Commentary: + +;; Simple emacs mode for editing .ninja files. +;; Just some syntax highlighting for now. + +;;; Code: + +(defvar ninja-keywords + `((,(concat "^" (regexp-opt '("rule" "build" "subninja" "include" + "pool" "default") + 'words)) + . font-lock-keyword-face) + ("\\([[:alnum:]_]+\\) =" 1 font-lock-variable-name-face) + ;; Variable expansion. + ("$[[:alnum:]_]+" . font-lock-variable-name-face) + ("${[[:alnum:]._]+}" . font-lock-variable-name-face) + ;; Rule names + ("rule +\\([[:alnum:]_.-]+\\)" 1 font-lock-function-name-face) + ;; Build Statement - highlight the rule used, + ;; allow for escaped $,: in outputs. + ("build +\\(?:[^:$\n]\\|$[:$]\\)+ *: *\\([[:alnum:]_.-]+\\)" + 1 font-lock-function-name-face))) + +(defvar ninja-mode-syntax-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\" "." table) + table) + "Syntax table used in `ninja-mode'.") + +(defun ninja-syntax-propertize (start end) + (save-match-data + (goto-char start) + (while (search-forward "#" end t) + (let ((match-pos (match-beginning 0))) + (when (and + ;; Is it the first non-white character on the line? + (eq match-pos (save-excursion (back-to-indentation) (point))) + (save-excursion + (goto-char (line-end-position 0)) + (or + ;; If we're continuting the previous line, it's not a + ;; comment. + (not (eq ?$ (char-before))) + ;; Except if the previous line is a comment as well, as the + ;; continuation dollar is ignored then. + (nth 4 (syntax-ppss))))) + (put-text-property match-pos (1+ match-pos) 'syntax-table '(11)) + (let ((line-end (line-end-position))) + ;; Avoid putting properties past the end of the buffer. + ;; Otherwise we get an `args-out-of-range' error. + (unless (= line-end (1+ (buffer-size))) + (put-text-property line-end (1+ line-end) 'syntax-table '(12))))))))) + +;;;###autoload +(define-derived-mode ninja-mode prog-mode "ninja" + (set (make-local-variable 'comment-start) "#") + (set (make-local-variable 'parse-sexp-lookup-properties) t) + (set (make-local-variable 'syntax-propertize-function) #'ninja-syntax-propertize) + (setq font-lock-defaults '(ninja-keywords))) + +;; Run ninja-mode for files ending in .ninja. +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.ninja$" . ninja-mode)) + +(provide 'ninja-mode) + +;;; ninja-mode.el ends here diff --git a/src/3rdparty/ninja/misc/ninja.vim b/src/3rdparty/ninja/misc/ninja.vim new file mode 100644 index 00000000000..190d9ceb8af --- /dev/null +++ b/src/3rdparty/ninja/misc/ninja.vim @@ -0,0 +1,83 @@ +" ninja build file syntax. +" Language: ninja build file as described at +" http://ninja-build.org/manual.html +" Version: 1.4 +" Last Change: 2014/05/13 +" Maintainer: Nicolas Weber +" Version 1.4 of this script is in the upstream vim repository and will be +" included in the next vim release. If you change this, please send your change +" upstream. + +" ninja lexer and parser are at +" https://github.com/ninja-build/ninja/blob/master/src/lexer.in.cc +" https://github.com/ninja-build/ninja/blob/master/src/manifest_parser.cc + +if exists("b:current_syntax") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +syn case match + +syn match ninjaComment /#.*/ contains=@Spell + +" Toplevel statements are the ones listed here and +" toplevel variable assignments (ident '=' value). +" lexer.in.cc, ReadToken() and manifest_parser.cc, Parse() +syn match ninjaKeyword "^build\>" +syn match ninjaKeyword "^rule\>" +syn match ninjaKeyword "^pool\>" +syn match ninjaKeyword "^default\>" +syn match ninjaKeyword "^include\>" +syn match ninjaKeyword "^subninja\>" + +" Both 'build' and 'rule' begin a variable scope that ends +" on the first line without indent. 'rule' allows only a +" limited set of magic variables, 'build' allows general +" let assignments. +" manifest_parser.cc, ParseRule() +syn region ninjaRule start="^rule" end="^\ze\S" contains=ALL transparent +syn keyword ninjaRuleCommand contained command deps depfile description generator + \ pool restat rspfile rspfile_content + +syn region ninjaPool start="^pool" end="^\ze\S" contains=ALL transparent +syn keyword ninjaPoolCommand contained depth + +" Strings are parsed as follows: +" lexer.in.cc, ReadEvalString() +" simple_varname = [a-zA-Z0-9_-]+; +" varname = [a-zA-Z0-9_.-]+; +" $$ -> $ +" $\n -> line continuation +" '$ ' -> escaped space +" $simple_varname -> variable +" ${varname} -> variable + +syn match ninjaDollar "\$\$" +syn match ninjaWrapLineOperator "\$$" +syn match ninjaSimpleVar "\$[a-zA-Z0-9_-]\+" +syn match ninjaVar "\${[a-zA-Z0-9_.-]\+}" + +" operators are: +" variable assignment = +" rule definition : +" implicit dependency | +" order-only dependency || +syn match ninjaOperator "\(=\|:\||\|||\)\ze\s" + +hi def link ninjaComment Comment +hi def link ninjaKeyword Keyword +hi def link ninjaRuleCommand Statement +hi def link ninjaPoolCommand Statement +hi def link ninjaDollar ninjaOperator +hi def link ninjaWrapLineOperator ninjaOperator +hi def link ninjaOperator Operator +hi def link ninjaSimpleVar ninjaVar +hi def link ninjaVar Identifier + +let b:current_syntax = "ninja" + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/src/3rdparty/ninja/misc/ninja_syntax.py b/src/3rdparty/ninja/misc/ninja_syntax.py new file mode 100644 index 00000000000..5c52ea23f85 --- /dev/null +++ b/src/3rdparty/ninja/misc/ninja_syntax.py @@ -0,0 +1,181 @@ +#!/usr/bin/python + +"""Python module for generating .ninja files. + +Note that this is emphatically not a required piece of Ninja; it's +just a helpful utility for build-file-generation systems that already +use Python. +""" + +import re +import textwrap + +def escape_path(word): + return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:') + +class Writer(object): + def __init__(self, output, width=78): + self.output = output + self.width = width + + def newline(self): + self.output.write('\n') + + def comment(self, text, has_path=False): + for line in textwrap.wrap(text, self.width - 2, break_long_words=False, + break_on_hyphens=False): + self.output.write('# ' + line + '\n') + + def variable(self, key, value, indent=0): + if value is None: + return + if isinstance(value, list): + value = ' '.join(filter(None, value)) # Filter out empty strings. + self._line('%s = %s' % (key, value), indent) + + def pool(self, name, depth): + self._line('pool %s' % name) + self.variable('depth', depth, indent=1) + + def rule(self, name, command, description=None, depfile=None, + generator=False, pool=None, restat=False, rspfile=None, + rspfile_content=None, deps=None): + self._line('rule %s' % name) + self.variable('command', command, indent=1) + if description: + self.variable('description', description, indent=1) + if depfile: + self.variable('depfile', depfile, indent=1) + if generator: + self.variable('generator', '1', indent=1) + if pool: + self.variable('pool', pool, indent=1) + if restat: + self.variable('restat', '1', indent=1) + if rspfile: + self.variable('rspfile', rspfile, indent=1) + if rspfile_content: + self.variable('rspfile_content', rspfile_content, indent=1) + if deps: + self.variable('deps', deps, indent=1) + + def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, + variables=None, implicit_outputs=None): + outputs = as_list(outputs) + out_outputs = [escape_path(x) for x in outputs] + all_inputs = [escape_path(x) for x in as_list(inputs)] + + if implicit: + implicit = [escape_path(x) for x in as_list(implicit)] + all_inputs.append('|') + all_inputs.extend(implicit) + if order_only: + order_only = [escape_path(x) for x in as_list(order_only)] + all_inputs.append('||') + all_inputs.extend(order_only) + if implicit_outputs: + implicit_outputs = [escape_path(x) + for x in as_list(implicit_outputs)] + out_outputs.append('|') + out_outputs.extend(implicit_outputs) + + self._line('build %s: %s' % (' '.join(out_outputs), + ' '.join([rule] + all_inputs))) + + if variables: + if isinstance(variables, dict): + iterator = iter(variables.items()) + else: + iterator = iter(variables) + + for key, val in iterator: + self.variable(key, val, indent=1) + + return outputs + + def include(self, path): + self._line('include %s' % path) + + def subninja(self, path): + self._line('subninja %s' % path) + + def default(self, paths): + self._line('default %s' % ' '.join(as_list(paths))) + + def _count_dollars_before_index(self, s, i): + """Returns the number of '$' characters right in front of s[i].""" + dollar_count = 0 + dollar_index = i - 1 + while dollar_index > 0 and s[dollar_index] == '$': + dollar_count += 1 + dollar_index -= 1 + return dollar_count + + def _line(self, text, indent=0): + """Write 'text' word-wrapped at self.width characters.""" + leading_space = ' ' * indent + while len(leading_space) + len(text) > self.width: + # The text is too wide; wrap if possible. + + # Find the rightmost space that would obey our width constraint and + # that's not an escaped space. + available_space = self.width - len(leading_space) - len(' $') + space = available_space + while True: + space = text.rfind(' ', 0, space) + if (space < 0 or + self._count_dollars_before_index(text, space) % 2 == 0): + break + + if space < 0: + # No such space; just use the first unescaped space we can find. + space = available_space - 1 + while True: + space = text.find(' ', space + 1) + if (space < 0 or + self._count_dollars_before_index(text, space) % 2 == 0): + break + if space < 0: + # Give up on breaking. + break + + self.output.write(leading_space + text[0:space] + ' $\n') + text = text[space+1:] + + # Subsequent lines are continuations, so indent them. + leading_space = ' ' * (indent+2) + + self.output.write(leading_space + text + '\n') + + def close(self): + self.output.close() + + +def as_list(input): + if input is None: + return [] + if isinstance(input, list): + return input + return [input] + + +def escape(string): + """Escape a string such that it can be embedded into a Ninja file without + further interpretation.""" + assert '\n' not in string, 'Ninja syntax does not allow newlines' + # We only have one special metacharacter: '$'. + return string.replace('$', '$$') + + +def expand(string, vars, local_vars={}): + """Expand a string containing $vars as Ninja would. + + Note: doesn't handle the full Ninja variable syntax, but it's enough + to make configure.py's use of it work. + """ + def exp(m): + var = m.group(1) + if var == '$': + return '$' + return local_vars.get(var, vars.get(var, '')) + return re.sub(r'\$(\$|\w*)', exp, string) diff --git a/src/3rdparty/ninja/misc/ninja_syntax_test.py b/src/3rdparty/ninja/misc/ninja_syntax_test.py new file mode 100755 index 00000000000..07e3ed3843f --- /dev/null +++ b/src/3rdparty/ninja/misc/ninja_syntax_test.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python + +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import ninja_syntax + +LONGWORD = 'a' * 10 +LONGWORDWITHSPACES = 'a'*5 + '$ ' + 'a'*5 +INDENT = ' ' + +class TestLineWordWrap(unittest.TestCase): + def setUp(self): + self.out = StringIO() + self.n = ninja_syntax.Writer(self.out, width=8) + + def test_single_long_word(self): + # We shouldn't wrap a single long word. + self.n._line(LONGWORD) + self.assertEqual(LONGWORD + '\n', self.out.getvalue()) + + def test_few_long_words(self): + # We should wrap a line where the second word is overlong. + self.n._line(' '.join(['x', LONGWORD, 'y'])) + self.assertEqual(' $\n'.join(['x', + INDENT + LONGWORD, + INDENT + 'y']) + '\n', + self.out.getvalue()) + + def test_comment_wrap(self): + # Filenames shoud not be wrapped + self.n.comment('Hello /usr/local/build-tools/bin') + self.assertEqual('# Hello\n# /usr/local/build-tools/bin\n', + self.out.getvalue()) + + def test_short_words_indented(self): + # Test that indent is taking into acount when breaking subsequent lines. + # The second line should not be ' to tree', as that's longer than the + # test layout width of 8. + self.n._line('line_one to tree') + self.assertEqual('''\ +line_one $ + to $ + tree +''', + self.out.getvalue()) + + def test_few_long_words_indented(self): + # Check wrapping in the presence of indenting. + self.n._line(' '.join(['x', LONGWORD, 'y']), indent=1) + self.assertEqual(' $\n'.join([' ' + 'x', + ' ' + INDENT + LONGWORD, + ' ' + INDENT + 'y']) + '\n', + self.out.getvalue()) + + def test_escaped_spaces(self): + self.n._line(' '.join(['x', LONGWORDWITHSPACES, 'y'])) + self.assertEqual(' $\n'.join(['x', + INDENT + LONGWORDWITHSPACES, + INDENT + 'y']) + '\n', + self.out.getvalue()) + + def test_fit_many_words(self): + self.n = ninja_syntax.Writer(self.out, width=78) + 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) + self.assertEqual('''\ + 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 +''', + self.out.getvalue()) + + def test_leading_space(self): + self.n = ninja_syntax.Writer(self.out, width=14) # force wrapping + self.n.variable('foo', ['', '-bar', '-somethinglong'], 0) + self.assertEqual('''\ +foo = -bar $ + -somethinglong +''', + self.out.getvalue()) + + def test_embedded_dollar_dollar(self): + self.n = ninja_syntax.Writer(self.out, width=15) # force wrapping + self.n.variable('foo', ['a$$b', '-somethinglong'], 0) + self.assertEqual('''\ +foo = a$$b $ + -somethinglong +''', + self.out.getvalue()) + + def test_two_embedded_dollar_dollars(self): + self.n = ninja_syntax.Writer(self.out, width=17) # force wrapping + self.n.variable('foo', ['a$$b', '-somethinglong'], 0) + self.assertEqual('''\ +foo = a$$b $ + -somethinglong +''', + self.out.getvalue()) + + def test_leading_dollar_dollar(self): + self.n = ninja_syntax.Writer(self.out, width=14) # force wrapping + self.n.variable('foo', ['$$b', '-somethinglong'], 0) + self.assertEqual('''\ +foo = $$b $ + -somethinglong +''', + self.out.getvalue()) + + def test_trailing_dollar_dollar(self): + self.n = ninja_syntax.Writer(self.out, width=14) # force wrapping + self.n.variable('foo', ['a$$', '-somethinglong'], 0) + self.assertEqual('''\ +foo = a$$ $ + -somethinglong +''', + self.out.getvalue()) + +class TestBuild(unittest.TestCase): + def setUp(self): + self.out = StringIO() + self.n = ninja_syntax.Writer(self.out) + + def test_variables_dict(self): + self.n.build('out', 'cc', 'in', variables={'name': 'value'}) + self.assertEqual('''\ +build out: cc in + name = value +''', + self.out.getvalue()) + + def test_variables_list(self): + self.n.build('out', 'cc', 'in', variables=[('name', 'value')]) + self.assertEqual('''\ +build out: cc in + name = value +''', + self.out.getvalue()) + + def test_implicit_outputs(self): + self.n.build('o', 'cc', 'i', implicit_outputs='io') + self.assertEqual('''\ +build o | io: cc i +''', + self.out.getvalue()) + +class TestExpand(unittest.TestCase): + def test_basic(self): + vars = {'x': 'X'} + self.assertEqual('foo', ninja_syntax.expand('foo', vars)) + + def test_var(self): + vars = {'xyz': 'XYZ'} + self.assertEqual('fooXYZ', ninja_syntax.expand('foo$xyz', vars)) + + def test_vars(self): + vars = {'x': 'X', 'y': 'YYY'} + self.assertEqual('XYYY', ninja_syntax.expand('$x$y', vars)) + + def test_space(self): + vars = {} + self.assertEqual('x y z', ninja_syntax.expand('x$ y$ z', vars)) + + def test_locals(self): + vars = {'x': 'a'} + local_vars = {'x': 'b'} + self.assertEqual('a', ninja_syntax.expand('$x', vars)) + self.assertEqual('b', ninja_syntax.expand('$x', vars, local_vars)) + + def test_double(self): + self.assertEqual('a b$c', ninja_syntax.expand('a$ b$$c', {})) + +if __name__ == '__main__': + unittest.main() diff --git a/src/3rdparty/ninja/misc/output_test.py b/src/3rdparty/ninja/misc/output_test.py new file mode 100755 index 00000000000..1dcde10b036 --- /dev/null +++ b/src/3rdparty/ninja/misc/output_test.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +"""Runs ./ninja and checks if the output is correct. + +In order to simulate a smart terminal it uses the 'script' command. +""" + +import os +import platform +import subprocess +import sys +import tempfile +import unittest + +default_env = dict(os.environ) +if 'NINJA_STATUS' in default_env: + del default_env['NINJA_STATUS'] +if 'CLICOLOR_FORCE' in default_env: + del default_env['CLICOLOR_FORCE'] +default_env['TERM'] = '' + +def run(build_ninja, flags='', pipe=False, env=default_env): + with tempfile.NamedTemporaryFile('w') as f: + f.write(build_ninja) + f.flush() + ninja_cmd = './ninja {} -f {}'.format(flags, f.name) + try: + if pipe: + output = subprocess.check_output([ninja_cmd], shell=True, env=env) + elif platform.system() == 'Darwin': + output = subprocess.check_output(['script', '-q', '/dev/null', 'bash', '-c', ninja_cmd], + env=env) + else: + output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'], + env=env) + except subprocess.CalledProcessError as err: + sys.stdout.buffer.write(err.output) + raise err + final_output = '' + for line in output.decode('utf-8').splitlines(True): + if len(line) > 0 and line[-1] == '\r': + continue + final_output += line.replace('\r', '') + return final_output + +class Output(unittest.TestCase): + def test_issue_1418(self): + self.assertEqual(run( +'''rule echo + command = sleep $delay && echo $out + description = echo $out + +build a: echo + delay = 3 +build b: echo + delay = 2 +build c: echo + delay = 1 +'''), +'''[1/3] echo c\x1b[K +c +[2/3] echo b\x1b[K +b +[3/3] echo a\x1b[K +a +''') + + def test_issue_1214(self): + print_red = '''rule echo + command = printf '\x1b[31mred\x1b[0m' + description = echo $out + +build a: echo +''' + # Only strip color when ninja's output is piped. + self.assertEqual(run(print_red), +'''[1/1] echo a\x1b[K +\x1b[31mred\x1b[0m +''') + self.assertEqual(run(print_red, pipe=True), +'''[1/1] echo a +red +''') + # Even in verbose mode, colors should still only be stripped when piped. + self.assertEqual(run(print_red, flags='-v'), +'''[1/1] printf '\x1b[31mred\x1b[0m' +\x1b[31mred\x1b[0m +''') + self.assertEqual(run(print_red, flags='-v', pipe=True), +'''[1/1] printf '\x1b[31mred\x1b[0m' +red +''') + + # CLICOLOR_FORCE=1 can be used to disable escape code stripping. + env = default_env.copy() + env['CLICOLOR_FORCE'] = '1' + self.assertEqual(run(print_red, pipe=True, env=env), +'''[1/1] echo a +\x1b[31mred\x1b[0m +''') + +if __name__ == '__main__': + unittest.main() diff --git a/src/3rdparty/ninja/misc/packaging/ninja.spec b/src/3rdparty/ninja/misc/packaging/ninja.spec new file mode 100644 index 00000000000..05f5a079a38 --- /dev/null +++ b/src/3rdparty/ninja/misc/packaging/ninja.spec @@ -0,0 +1,42 @@ +Summary: Ninja is a small build system with a focus on speed. +Name: ninja +Version: %{ver} +Release: %{rel}%{?dist} +Group: Development/Tools +License: Apache 2.0 +URL: https://github.com/ninja-build/ninja +Source0: %{name}-%{version}-%{rel}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{rel} + +BuildRequires: asciidoc + +%description +Ninja is yet another build system. It takes as input the interdependencies of files (typically source code and output executables) and +orchestrates building them, quickly. + +Ninja joins a sea of other build systems. Its distinguishing goal is to be fast. It is born from my work on the Chromium browser project, +which has over 30,000 source files and whose other build systems (including one built from custom non-recursive Makefiles) can take ten +seconds to start building after changing one file. Ninja is under a second. + +%prep +%setup -q -n %{name}-%{version}-%{rel} + +%build +echo Building.. +./configure.py --bootstrap +./ninja manual + +%install +mkdir -p %{buildroot}%{_bindir} %{buildroot}%{_docdir} +cp -p ninja %{buildroot}%{_bindir}/ + +%files +%defattr(-, root, root) +%doc COPYING README doc/manual.html +%{_bindir}/* + +%clean +rm -rf %{buildroot} + +#The changelog is built automatically from Git history +%changelog diff --git a/src/3rdparty/ninja/misc/packaging/rpmbuild.sh b/src/3rdparty/ninja/misc/packaging/rpmbuild.sh new file mode 100755 index 00000000000..9b74c6588c9 --- /dev/null +++ b/src/3rdparty/ninja/misc/packaging/rpmbuild.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +echo Building ninja RPMs.. +GITROOT=$(git rev-parse --show-toplevel) +cd $GITROOT + +VER=1.0 +REL=$(git rev-parse --short HEAD)git +RPMTOPDIR=$GITROOT/rpm-build +echo "Ver: $VER, Release: $REL" + +# Create tarball +mkdir -p $RPMTOPDIR/{SOURCES,SPECS} +git archive --format=tar --prefix=ninja-${VER}-${REL}/ HEAD | gzip -c > $RPMTOPDIR/SOURCES/ninja-${VER}-${REL}.tar.gz + +# Convert git log to RPM's ChangeLog format (shown with rpm -qp --changelog ) +sed -e "s/%{ver}/$VER/" -e "s/%{rel}/$REL/" misc/packaging/ninja.spec > $RPMTOPDIR/SPECS/ninja.spec +git log --format="* %cd %aN%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $RPMTOPDIR/SPECS/ninja.spec + +# Build SRC and binary RPMs +rpmbuild --quiet \ + --define "_topdir $RPMTOPDIR" \ + --define "_rpmdir $PWD" \ + --define "_srcrpmdir $PWD" \ + --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ + -ba $RPMTOPDIR/SPECS/ninja.spec && + +rm -rf $RPMTOPDIR && +echo Done diff --git a/src/3rdparty/ninja/misc/write_fake_manifests.py b/src/3rdparty/ninja/misc/write_fake_manifests.py new file mode 100644 index 00000000000..b3594de0bbc --- /dev/null +++ b/src/3rdparty/ninja/misc/write_fake_manifests.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python + +"""Writes large manifest files, for manifest parser performance testing. + +The generated manifest files are (eerily) similar in appearance and size to the +ones used in the Chromium project. + +Usage: + python misc/write_fake_manifests.py outdir # Will run for about 5s. + +The program contains a hardcoded random seed, so it will generate the same +output every time it runs. By changing the seed, it's easy to generate many +different sets of manifest files. +""" + +import argparse +import contextlib +import os +import random +import sys + +import ninja_syntax + + +def paretoint(avg, alpha): + """Returns a random integer that's avg on average, following a power law. + alpha determines the shape of the power curve. alpha has to be larger + than 1. The closer alpha is to 1, the higher the variation of the returned + numbers.""" + return int(random.paretovariate(alpha) * avg / (alpha / (alpha - 1))) + + +# Based on http://neugierig.org/software/chromium/class-name-generator.html +def moar(avg_options, p_suffix): + kStart = ['render', 'web', 'browser', 'tab', 'content', 'extension', 'url', + 'file', 'sync', 'content', 'http', 'profile'] + kOption = ['view', 'host', 'holder', 'container', 'impl', 'ref', + 'delegate', 'widget', 'proxy', 'stub', 'context', + 'manager', 'master', 'watcher', 'service', 'file', 'data', + 'resource', 'device', 'info', 'provider', 'internals', 'tracker', + 'api', 'layer'] + kOS = ['win', 'mac', 'aura', 'linux', 'android', 'unittest', 'browsertest'] + num_options = min(paretoint(avg_options, alpha=4), 5) + # The original allows kOption to repeat as long as no consecutive options + # repeat. This version doesn't allow any option repetition. + name = [random.choice(kStart)] + random.sample(kOption, num_options) + if random.random() < p_suffix: + name.append(random.choice(kOS)) + return '_'.join(name) + + +class GenRandom(object): + def __init__(self, src_dir): + self.seen_names = set([None]) + self.seen_defines = set([None]) + self.src_dir = src_dir + + def _unique_string(self, seen, avg_options=1.3, p_suffix=0.1): + s = None + while s in seen: + s = moar(avg_options, p_suffix) + seen.add(s) + return s + + def _n_unique_strings(self, n): + seen = set([None]) + return [self._unique_string(seen, avg_options=3, p_suffix=0.4) + for _ in xrange(n)] + + def target_name(self): + return self._unique_string(p_suffix=0, seen=self.seen_names) + + def path(self): + return os.path.sep.join([ + self._unique_string(self.seen_names, avg_options=1, p_suffix=0) + for _ in xrange(1 + paretoint(0.6, alpha=4))]) + + def src_obj_pairs(self, path, name): + num_sources = paretoint(55, alpha=2) + 1 + return [(os.path.join(self.src_dir, path, s + '.cc'), + os.path.join('obj', path, '%s.%s.o' % (name, s))) + for s in self._n_unique_strings(num_sources)] + + def defines(self): + return [ + '-DENABLE_' + self._unique_string(self.seen_defines).upper() + for _ in xrange(paretoint(20, alpha=3))] + + +LIB, EXE = 0, 1 +class Target(object): + def __init__(self, gen, kind): + self.name = gen.target_name() + self.dir_path = gen.path() + self.ninja_file_path = os.path.join( + 'obj', self.dir_path, self.name + '.ninja') + self.src_obj_pairs = gen.src_obj_pairs(self.dir_path, self.name) + if kind == LIB: + self.output = os.path.join('lib' + self.name + '.a') + elif kind == EXE: + self.output = os.path.join(self.name) + self.defines = gen.defines() + self.deps = [] + self.kind = kind + self.has_compile_depends = random.random() < 0.4 + + +def write_target_ninja(ninja, target, src_dir): + compile_depends = None + if target.has_compile_depends: + compile_depends = os.path.join( + 'obj', target.dir_path, target.name + '.stamp') + ninja.build(compile_depends, 'stamp', target.src_obj_pairs[0][0]) + ninja.newline() + + ninja.variable('defines', target.defines) + ninja.variable('includes', '-I' + src_dir) + ninja.variable('cflags', ['-Wall', '-fno-rtti', '-fno-exceptions']) + ninja.newline() + + for src, obj in target.src_obj_pairs: + ninja.build(obj, 'cxx', src, implicit=compile_depends) + ninja.newline() + + deps = [dep.output for dep in target.deps] + libs = [dep.output for dep in target.deps if dep.kind == LIB] + if target.kind == EXE: + ninja.variable('libs', libs) + if sys.platform == "darwin": + ninja.variable('ldflags', '-Wl,-pie') + link = { LIB: 'alink', EXE: 'link'}[target.kind] + ninja.build(target.output, link, [obj for _, obj in target.src_obj_pairs], + implicit=deps) + + +def write_sources(target, root_dir): + need_main = target.kind == EXE + + includes = [] + + # Include siblings. + for cc_filename, _ in target.src_obj_pairs: + h_filename = os.path.basename(os.path.splitext(cc_filename)[0] + '.h') + includes.append(h_filename) + + # Include deps. + for dep in target.deps: + for cc_filename, _ in dep.src_obj_pairs: + h_filename = os.path.basename( + os.path.splitext(cc_filename)[0] + '.h') + includes.append("%s/%s" % (dep.dir_path, h_filename)) + + for cc_filename, _ in target.src_obj_pairs: + cc_path = os.path.join(root_dir, cc_filename) + h_path = os.path.splitext(cc_path)[0] + '.h' + namespace = os.path.basename(target.dir_path) + class_ = os.path.splitext(os.path.basename(cc_filename))[0] + try: + os.makedirs(os.path.dirname(cc_path)) + except OSError: + pass + + with open(h_path, 'w') as f: + f.write('namespace %s { struct %s { %s(); }; }' % (namespace, + class_, class_)) + with open(cc_path, 'w') as f: + for include in includes: + f.write('#include "%s"\n' % include) + f.write('\n') + f.write('namespace %s { %s::%s() {} }' % (namespace, + class_, class_)) + + if need_main: + f.write('int main(int argc, char **argv) {}\n') + need_main = False + +def write_master_ninja(master_ninja, targets): + """Writes master build.ninja file, referencing all given subninjas.""" + master_ninja.variable('cxx', 'c++') + master_ninja.variable('ld', '$cxx') + if sys.platform == 'darwin': + master_ninja.variable('alink', 'libtool -static') + else: + master_ninja.variable('alink', 'ar rcs') + master_ninja.newline() + + master_ninja.pool('link_pool', depth=4) + master_ninja.newline() + + master_ninja.rule('cxx', description='CXX $out', + command='$cxx -MMD -MF $out.d $defines $includes $cflags -c $in -o $out', + depfile='$out.d', deps='gcc') + master_ninja.rule('alink', description='ARCHIVE $out', + command='rm -f $out && $alink -o $out $in') + master_ninja.rule('link', description='LINK $out', pool='link_pool', + command='$ld $ldflags -o $out $in $libs') + master_ninja.rule('stamp', description='STAMP $out', command='touch $out') + master_ninja.newline() + + for target in targets: + master_ninja.subninja(target.ninja_file_path) + master_ninja.newline() + + master_ninja.comment('Short names for targets.') + for target in targets: + if target.name != target.output: + master_ninja.build(target.name, 'phony', target.output) + master_ninja.newline() + + master_ninja.build('all', 'phony', [target.output for target in targets]) + master_ninja.default('all') + + +@contextlib.contextmanager +def FileWriter(path): + """Context manager for a ninja_syntax object writing to a file.""" + try: + os.makedirs(os.path.dirname(path)) + except OSError: + pass + f = open(path, 'w') + yield ninja_syntax.Writer(f) + f.close() + + +def random_targets(num_targets, src_dir): + gen = GenRandom(src_dir) + + # N-1 static libraries, and 1 executable depending on all of them. + targets = [Target(gen, LIB) for i in xrange(num_targets - 1)] + for i in range(len(targets)): + targets[i].deps = [t for t in targets[0:i] if random.random() < 0.05] + + last_target = Target(gen, EXE) + last_target.deps = targets[:] + last_target.src_obj_pairs = last_target.src_obj_pairs[0:10] # Trim. + targets.append(last_target) + return targets + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--sources', nargs="?", const="src", + help='write sources to directory (relative to output directory)') + parser.add_argument('-t', '--targets', type=int, default=1500, + help='number of targets (default: 1500)') + parser.add_argument('-S', '--seed', type=int, help='random seed', + default=12345) + parser.add_argument('outdir', help='output directory') + args = parser.parse_args() + root_dir = args.outdir + + random.seed(args.seed) + + do_write_sources = args.sources is not None + src_dir = args.sources if do_write_sources else "src" + + targets = random_targets(args.targets, src_dir) + for target in targets: + with FileWriter(os.path.join(root_dir, target.ninja_file_path)) as n: + write_target_ninja(n, target, src_dir) + + if do_write_sources: + write_sources(target, root_dir) + + with FileWriter(os.path.join(root_dir, 'build.ninja')) as master_ninja: + master_ninja.width = 120 + write_master_ninja(master_ninja, targets) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/3rdparty/ninja/misc/zsh-completion b/src/3rdparty/ninja/misc/zsh-completion new file mode 100644 index 00000000000..bf23face5f8 --- /dev/null +++ b/src/3rdparty/ninja/misc/zsh-completion @@ -0,0 +1,72 @@ +#compdef ninja +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Add the following to your .zshrc to tab-complete ninja targets +# . path/to/ninja/misc/zsh-completion + +__get_targets() { + dir="." + if [ -n "${opt_args[-C]}" ]; + then + eval dir="${opt_args[-C]}" + fi + file="build.ninja" + if [ -n "${opt_args[-f]}" ]; + then + eval file="${opt_args[-f]}" + fi + targets_command="ninja -f \"${file}\" -C \"${dir}\" -t targets all" + eval ${targets_command} 2>/dev/null | cut -d: -f1 +} + +__get_tools() { + ninja -t list 2>/dev/null | while read -r a b; do echo $a; done | tail -n +2 +} + +__get_modes() { + ninja -d list 2>/dev/null | while read -r a b; do echo $a; done | tail -n +2 | sed '$d' +} + +__modes() { + local -a modes + modes=(${(fo)"$(__get_modes)"}) + _describe 'modes' modes +} + +__tools() { + local -a tools + tools=(${(fo)"$(__get_tools)"}) + _describe 'tools' tools +} + +__targets() { + local -a targets + targets=(${(fo)"$(__get_targets)"}) + _describe 'targets' targets +} + +_arguments \ + {-h,--help}'[Show help]' \ + '--version[Print ninja version]' \ + '-C+[Change to directory before doing anything else]:directories:_directories' \ + '-f+[Specify input build file (default=build.ninja)]:files:_files' \ + '-j+[Run N jobs in parallel (default=number of CPUs available)]:number of jobs' \ + '-l+[Do not start new jobs if the load average is greater than N]:number of jobs' \ + '-k+[Keep going until N jobs fail (default=1)]:number of jobs' \ + '-n[Dry run (do not run commands but act like they succeeded)]' \ + '-v[Show all command lines while building]' \ + '-d+[Enable debugging (use -d list to list modes)]:modes:__modes' \ + '-t+[Run a subtool (use -t list to list subtools)]:tools:__tools' \ + '*::targets:__targets' diff --git a/src/3rdparty/ninja/src/browse.cc b/src/3rdparty/ninja/src/browse.cc new file mode 100644 index 00000000000..14900f8464b --- /dev/null +++ b/src/3rdparty/ninja/src/browse.cc @@ -0,0 +1,73 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "browse.h" + +#include +#include +#include +#include + +#include "build/browse_py.h" + +void RunBrowsePython(State* state, const char* ninja_command, + const char* input_file, int argc, char* argv[]) { + // Fork off a Python process and have it run our code via its stdin. + // (Actually the Python process becomes the parent.) + int pipefd[2]; + if (pipe(pipefd) < 0) { + perror("ninja: pipe"); + return; + } + + pid_t pid = fork(); + if (pid < 0) { + perror("ninja: fork"); + return; + } + + if (pid > 0) { // Parent. + close(pipefd[1]); + do { + if (dup2(pipefd[0], 0) < 0) { + perror("ninja: dup2"); + break; + } + + std::vector command; + command.push_back(NINJA_PYTHON); + command.push_back("-"); + command.push_back("--ninja-command"); + command.push_back(ninja_command); + command.push_back("-f"); + command.push_back(input_file); + for (int i = 0; i < argc; i++) { + command.push_back(argv[i]); + } + command.push_back(NULL); + execvp(command[0], (char**)&command[0]); + perror("ninja: execvp"); + } while (false); + _exit(1); + } else { // Child. + close(pipefd[0]); + + // Write the script file into the stdin of the Python process. + ssize_t len = write(pipefd[1], kBrowsePy, sizeof(kBrowsePy)); + if (len < (ssize_t)sizeof(kBrowsePy)) + perror("ninja: write"); + close(pipefd[1]); + exit(0); + } +} diff --git a/src/3rdparty/ninja/src/browse.h b/src/3rdparty/ninja/src/browse.h new file mode 100644 index 00000000000..8d6d285711f --- /dev/null +++ b/src/3rdparty/ninja/src/browse.h @@ -0,0 +1,28 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_BROWSE_H_ +#define NINJA_BROWSE_H_ + +struct State; + +/// Run in "browse" mode, which execs a Python webserver. +/// \a ninja_command is the command used to invoke ninja. +/// \a args are the number of arguments to be passed to the Python script. +/// \a argv are arguments to be passed to the Python script. +/// This function does not return if it runs successfully. +void RunBrowsePython(State* state, const char* ninja_command, + const char* input_file, int argc, char* argv[]); + +#endif // NINJA_BROWSE_H_ diff --git a/src/3rdparty/ninja/src/browse.py b/src/3rdparty/ninja/src/browse.py new file mode 100755 index 00000000000..64a16f2a276 --- /dev/null +++ b/src/3rdparty/ninja/src/browse.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python +# +# Copyright 2001 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Simple web server for browsing dependency graph data. + +This script is inlined into the final executable and spawned by +it when needed. +""" + +from __future__ import print_function + +try: + import http.server as httpserver +except ImportError: + import BaseHTTPServer as httpserver +import argparse +import cgi +import os +import socket +import subprocess +import sys +import webbrowser +try: + from urllib.request import unquote +except ImportError: + from urllib2 import unquote +from collections import namedtuple + +Node = namedtuple('Node', ['inputs', 'rule', 'target', 'outputs']) + +# Ideally we'd allow you to navigate to a build edge or a build node, +# with appropriate views for each. But there's no way to *name* a build +# edge so we can only display nodes. +# +# For a given node, it has at most one input edge, which has n +# different inputs. This becomes node.inputs. (We leave out the +# outputs of the input edge due to what follows.) The node can have +# multiple dependent output edges. Rather than attempting to display +# those, they are summarized by taking the union of all their outputs. +# +# This means there's no single view that shows you all inputs and outputs +# of an edge. But I think it's less confusing than alternatives. + +def match_strip(line, prefix): + if not line.startswith(prefix): + return (False, line) + return (True, line[len(prefix):]) + +def html_escape(text): + return cgi.escape(text, quote=True) + +def parse(text): + lines = iter(text.split('\n')) + + target = None + rule = None + inputs = [] + outputs = [] + + try: + target = next(lines)[:-1] # strip trailing colon + + line = next(lines) + (match, rule) = match_strip(line, ' input: ') + if match: + (match, line) = match_strip(next(lines), ' ') + while match: + type = None + (match, line) = match_strip(line, '| ') + if match: + type = 'implicit' + (match, line) = match_strip(line, '|| ') + if match: + type = 'order-only' + inputs.append((line, type)) + (match, line) = match_strip(next(lines), ' ') + + match, _ = match_strip(line, ' outputs:') + if match: + (match, line) = match_strip(next(lines), ' ') + while match: + outputs.append(line) + (match, line) = match_strip(next(lines), ' ') + except StopIteration: + pass + + return Node(inputs, rule, target, outputs) + +def create_page(body): + return ''' + +''' + body + +def generate_html(node): + document = ['

%s

' % html_escape(node.target)] + + if node.inputs: + document.append('

target is built using rule %s of

' % + html_escape(node.rule)) + if len(node.inputs) > 0: + document.append('
') + for input, type in sorted(node.inputs): + extra = '' + if type: + extra = ' (%s)' % html_escape(type) + document.append('%s%s
' % + (html_escape(input), html_escape(input), extra)) + document.append('
') + + if node.outputs: + document.append('

dependent edges build:

') + document.append('
') + for output in sorted(node.outputs): + document.append('%s
' % + (html_escape(output), html_escape(output))) + document.append('
') + + return '\n'.join(document) + +def ninja_dump(target): + cmd = [args.ninja_command, '-f', args.f, '-t', 'query', target] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + return proc.communicate() + (proc.returncode,) + +class RequestHandler(httpserver.BaseHTTPRequestHandler): + def do_GET(self): + assert self.path[0] == '/' + target = unquote(self.path[1:]) + + if target == '': + self.send_response(302) + self.send_header('Location', '?' + args.initial_target) + self.end_headers() + return + + if not target.startswith('?'): + self.send_response(404) + self.end_headers() + return + target = target[1:] + + ninja_output, ninja_error, exit_code = ninja_dump(target) + if exit_code == 0: + page_body = generate_html(parse(ninja_output.strip())) + else: + # Relay ninja's error message. + page_body = '

%s

' % html_escape(ninja_error) + + self.send_response(200) + self.end_headers() + self.wfile.write(create_page(page_body).encode('utf-8')) + + def log_message(self, format, *args): + pass # Swallow console spam. + +parser = argparse.ArgumentParser(prog='ninja -t browse') +parser.add_argument('--port', '-p', default=8000, type=int, + help='Port number to use (default %(default)d)') +parser.add_argument('--hostname', '-a', default='localhost', type=str, + help='Hostname to bind to (default %(default)s)') +parser.add_argument('--no-browser', action='http://github.com/store_true', + help='Do not open a webbrowser on startup.') + +parser.add_argument('--ninja-command', default='ninja', + help='Path to ninja binary (default %(default)s)') +parser.add_argument('-f', default='build.ninja', + help='Path to build.ninja file (default %(default)s)') +parser.add_argument('initial_target', default='all', nargs='?', + help='Initial target to show (default %(default)s)') + +args = parser.parse_args() +port = args.port +hostname = args.hostname +httpd = httpserver.HTTPServer((hostname,port), RequestHandler) +try: + if hostname == "": + hostname = socket.gethostname() + print('Web server running on %s:%d, ctl-C to abort...' % (hostname,port) ) + print('Web server pid %d' % os.getpid(), file=sys.stderr ) + if not args.no_browser: + webbrowser.open_new('http://%s:%s' % (hostname, port) ) + httpd.serve_forever() +except KeyboardInterrupt: + print() + pass # Swallow console spam. + + diff --git a/src/3rdparty/ninja/src/build.cc b/src/3rdparty/ninja/src/build.cc new file mode 100644 index 00000000000..61ef0e849ad --- /dev/null +++ b/src/3rdparty/ninja/src/build.cc @@ -0,0 +1,929 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "build.h" + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#if defined(__SVR4) && defined(__sun) +#include +#endif + +#include "build_log.h" +#include "clparser.h" +#include "debug_flags.h" +#include "depfile_parser.h" +#include "deps_log.h" +#include "disk_interface.h" +#include "graph.h" +#include "state.h" +#include "subprocess.h" +#include "util.h" + +namespace { + +/// A CommandRunner that doesn't actually run the commands. +struct DryRunCommandRunner : public CommandRunner { + virtual ~DryRunCommandRunner() {} + + // Overridden from CommandRunner: + virtual bool CanRunMore(); + virtual bool StartCommand(Edge* edge); + virtual bool WaitForCommand(Result* result); + + private: + queue finished_; +}; + +bool DryRunCommandRunner::CanRunMore() { + return true; +} + +bool DryRunCommandRunner::StartCommand(Edge* edge) { + finished_.push(edge); + return true; +} + +bool DryRunCommandRunner::WaitForCommand(Result* result) { + if (finished_.empty()) + return false; + + result->status = ExitSuccess; + result->edge = finished_.front(); + finished_.pop(); + return true; +} + +} // namespace + +BuildStatus::BuildStatus(const BuildConfig& config) + : config_(config), + start_time_millis_(GetTimeMillis()), + started_edges_(0), finished_edges_(0), total_edges_(0), + progress_status_format_(NULL), + overall_rate_(), current_rate_(config.parallelism) { + + // Don't do anything fancy in verbose mode. + if (config_.verbosity != BuildConfig::NORMAL) + printer_.set_smart_terminal(false); + + progress_status_format_ = getenv("NINJA_STATUS"); + if (!progress_status_format_) + progress_status_format_ = "[%f/%t] "; +} + +void BuildStatus::PlanHasTotalEdges(int total) { + total_edges_ = total; +} + +void BuildStatus::BuildEdgeStarted(Edge* edge) { + int start_time = (int)(GetTimeMillis() - start_time_millis_); + running_edges_.insert(make_pair(edge, start_time)); + ++started_edges_; + + if (edge->use_console() || printer_.is_smart_terminal()) + PrintStatus(edge, kEdgeStarted); + + if (edge->use_console()) + printer_.SetConsoleLocked(true); +} + +void BuildStatus::BuildEdgeFinished(Edge* edge, + bool success, + const string& output, + int* start_time, + int* end_time) { + int64_t now = GetTimeMillis(); + + ++finished_edges_; + + RunningEdgeMap::iterator i = running_edges_.find(edge); + *start_time = i->second; + *end_time = (int)(now - start_time_millis_); + running_edges_.erase(i); + + if (edge->use_console()) + printer_.SetConsoleLocked(false); + + if (config_.verbosity == BuildConfig::QUIET) + return; + + if (!edge->use_console()) + PrintStatus(edge, kEdgeFinished); + + // Print the command that is spewing before printing its output. + if (!success) { + string outputs; + for (vector::const_iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) + outputs += (*o)->path() + " "; + + printer_.PrintOnNewLine("FAILED: " + outputs + "\n"); + printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n"); + } + + if (!output.empty()) { + // ninja sets stdout and stderr of subprocesses to a pipe, to be able to + // check if the output is empty. Some compilers, e.g. clang, check + // isatty(stderr) to decide if they should print colored output. + // To make it possible to use colored output with ninja, subprocesses should + // be run with a flag that forces them to always print color escape codes. + // To make sure these escape codes don't show up in a file if ninja's output + // is piped to a file, ninja strips ansi escape codes again if it's not + // writing to a |smart_terminal_|. + // (Launching subprocesses in pseudo ttys doesn't work because there are + // only a few hundred available on some systems, and ninja can launch + // thousands of parallel compile commands.) + // TODO: There should be a flag to disable escape code stripping. + string final_output; + if (!printer_.is_smart_terminal()) + final_output = StripAnsiEscapeCodes(output); + else + final_output = output; + +#ifdef _WIN32 + // Fix extra CR being added on Windows, writing out CR CR LF (#773) + _setmode(_fileno(stdout), _O_BINARY); // Begin Windows extra CR fix +#endif + + printer_.PrintOnNewLine(final_output); + +#ifdef _WIN32 + _setmode(_fileno(stdout), _O_TEXT); // End Windows extra CR fix +#endif + } +} + +void BuildStatus::BuildStarted() { + overall_rate_.Restart(); + current_rate_.Restart(); +} + +void BuildStatus::BuildFinished() { + printer_.SetConsoleLocked(false); + printer_.PrintOnNewLine(""); +} + +string BuildStatus::FormatProgressStatus( + const char* progress_status_format, EdgeStatus status) const { + string out; + char buf[32]; + int percent; + for (const char* s = progress_status_format; *s != '\0'; ++s) { + if (*s == '%') { + ++s; + switch (*s) { + case '%': + out.push_back('%'); + break; + + // Started edges. + case 's': + snprintf(buf, sizeof(buf), "%d", started_edges_); + out += buf; + break; + + // Total edges. + case 't': + snprintf(buf, sizeof(buf), "%d", total_edges_); + out += buf; + break; + + // Running edges. + case 'r': { + int running_edges = started_edges_ - finished_edges_; + // count the edge that just finished as a running edge + if (status == kEdgeFinished) + running_edges++; + snprintf(buf, sizeof(buf), "%d", running_edges); + out += buf; + break; + } + + // Unstarted edges. + case 'u': + snprintf(buf, sizeof(buf), "%d", total_edges_ - started_edges_); + out += buf; + break; + + // Finished edges. + case 'f': + snprintf(buf, sizeof(buf), "%d", finished_edges_); + out += buf; + break; + + // Overall finished edges per second. + case 'o': + overall_rate_.UpdateRate(finished_edges_); + SnprintfRate(overall_rate_.rate(), buf, "%.1f"); + out += buf; + break; + + // Current rate, average over the last '-j' jobs. + case 'c': + current_rate_.UpdateRate(finished_edges_); + SnprintfRate(current_rate_.rate(), buf, "%.1f"); + out += buf; + break; + + // Percentage + case 'p': + percent = (100 * finished_edges_) / total_edges_; + snprintf(buf, sizeof(buf), "%3i%%", percent); + out += buf; + break; + + case 'e': { + double elapsed = overall_rate_.Elapsed(); + snprintf(buf, sizeof(buf), "%.3f", elapsed); + out += buf; + break; + } + + default: + Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s); + return ""; + } + } else { + out.push_back(*s); + } + } + + return out; +} + +void BuildStatus::PrintStatus(Edge* edge, EdgeStatus status) { + if (config_.verbosity == BuildConfig::QUIET) + return; + + bool force_full_command = config_.verbosity == BuildConfig::VERBOSE; + + string to_print = edge->GetBinding("description"); + if (to_print.empty() || force_full_command) + to_print = edge->GetBinding("command"); + + to_print = FormatProgressStatus(progress_status_format_, status) + to_print; + + printer_.Print(to_print, + force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE); +} + +Plan::Plan() : command_edges_(0), wanted_edges_(0) {} + +void Plan::Reset() { + command_edges_ = 0; + wanted_edges_ = 0; + ready_.clear(); + want_.clear(); +} + +bool Plan::AddTarget(Node* node, string* err) { + return AddSubTarget(node, NULL, err); +} + +bool Plan::AddSubTarget(Node* node, Node* dependent, string* err) { + Edge* edge = node->in_edge(); + if (!edge) { // Leaf node. + if (node->dirty()) { + string referenced; + if (dependent) + referenced = ", needed by '" + dependent->path() + "',"; + *err = "'" + node->path() + "'" + referenced + " missing " + "and no known rule to make it"; + } + return false; + } + + if (edge->outputs_ready()) + return false; // Don't need to do anything. + + // If an entry in want_ does not already exist for edge, create an entry which + // maps to false, indicating that we do not want to build this entry itself. + pair::iterator, bool> want_ins = + want_.insert(make_pair(edge, false)); + bool& want = want_ins.first->second; + + // If we do need to build edge and we haven't already marked it as wanted, + // mark it now. + if (node->dirty() && !want) { + want = true; + ++wanted_edges_; + if (edge->AllInputsReady()) + ScheduleWork(edge); + if (!edge->is_phony()) + ++command_edges_; + } + + if (!want_ins.second) + return true; // We've already processed the inputs. + + for (vector::iterator i = edge->inputs_.begin(); + i != edge->inputs_.end(); ++i) { + if (!AddSubTarget(*i, node, err) && !err->empty()) + return false; + } + + return true; +} + +Edge* Plan::FindWork() { + if (ready_.empty()) + return NULL; + set::iterator e = ready_.begin(); + Edge* edge = *e; + ready_.erase(e); + return edge; +} + +void Plan::ScheduleWork(Edge* edge) { + set::iterator e = ready_.lower_bound(edge); + if (e != ready_.end() && !ready_.key_comp()(edge, *e)) { + // This edge has already been scheduled. We can get here again if an edge + // and one of its dependencies share an order-only input, or if a node + // duplicates an out edge (see https://github.com/ninja-build/ninja/pull/519). + // Avoid scheduling the work again. + return; + } + + Pool* pool = edge->pool(); + if (pool->ShouldDelayEdge()) { + pool->DelayEdge(edge); + pool->RetrieveReadyEdges(&ready_); + } else { + pool->EdgeScheduled(*edge); + ready_.insert(e, edge); + } +} + +void Plan::EdgeFinished(Edge* edge, EdgeResult result) { + map::iterator e = want_.find(edge); + assert(e != want_.end()); + bool directly_wanted = e->second; + + // See if this job frees up any delayed jobs. + if (directly_wanted) + edge->pool()->EdgeFinished(*edge); + edge->pool()->RetrieveReadyEdges(&ready_); + + // The rest of this function only applies to successful commands. + if (result != kEdgeSucceeded) + return; + + if (directly_wanted) + --wanted_edges_; + want_.erase(e); + edge->outputs_ready_ = true; + + // Check off any nodes we were waiting for with this edge. + for (vector::iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) { + NodeFinished(*o); + } +} + +void Plan::NodeFinished(Node* node) { + // See if we we want any edges from this node. + for (vector::const_iterator oe = node->out_edges().begin(); + oe != node->out_edges().end(); ++oe) { + map::iterator want_e = want_.find(*oe); + if (want_e == want_.end()) + continue; + + // See if the edge is now ready. + if ((*oe)->AllInputsReady()) { + if (want_e->second) { + ScheduleWork(*oe); + } else { + // We do not need to build this edge, but we might need to build one of + // its dependents. + EdgeFinished(*oe, kEdgeSucceeded); + } + } + } +} + +bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { + node->set_dirty(false); + + for (vector::const_iterator oe = node->out_edges().begin(); + oe != node->out_edges().end(); ++oe) { + // Don't process edges that we don't actually want. + map::iterator want_e = want_.find(*oe); + if (want_e == want_.end() || !want_e->second) + continue; + + // Don't attempt to clean an edge if it failed to load deps. + if ((*oe)->deps_missing_) + continue; + + // If all non-order-only inputs for this edge are now clean, + // we might have changed the dirty state of the outputs. + vector::iterator + begin = (*oe)->inputs_.begin(), + end = (*oe)->inputs_.end() - (*oe)->order_only_deps_; + if (find_if(begin, end, mem_fun(&Node::dirty)) == end) { + // Recompute most_recent_input. + Node* most_recent_input = NULL; + for (vector::iterator i = begin; i != end; ++i) { + if (!most_recent_input || (*i)->mtime() > most_recent_input->mtime()) + most_recent_input = *i; + } + + // Now, this edge is dirty if any of the outputs are dirty. + // If the edge isn't dirty, clean the outputs and mark the edge as not + // wanted. + bool outputs_dirty = false; + if (!scan->RecomputeOutputsDirty(*oe, most_recent_input, + &outputs_dirty, err)) { + return false; + } + if (!outputs_dirty) { + for (vector::iterator o = (*oe)->outputs_.begin(); + o != (*oe)->outputs_.end(); ++o) { + if (!CleanNode(scan, *o, err)) + return false; + } + + want_e->second = false; + --wanted_edges_; + if (!(*oe)->is_phony()) + --command_edges_; + } + } + } + return true; +} + +void Plan::Dump() { + printf("pending: %d\n", (int)want_.size()); + for (map::iterator e = want_.begin(); e != want_.end(); ++e) { + if (e->second) + printf("want "); + e->first->Dump(); + } + printf("ready: %d\n", (int)ready_.size()); +} + +struct RealCommandRunner : public CommandRunner { + explicit RealCommandRunner(const BuildConfig& config) : config_(config) {} + virtual ~RealCommandRunner() {} + virtual bool CanRunMore(); + virtual bool StartCommand(Edge* edge); + virtual bool WaitForCommand(Result* result); + virtual vector GetActiveEdges(); + virtual void Abort(); + + const BuildConfig& config_; + SubprocessSet subprocs_; + map subproc_to_edge_; +}; + +vector RealCommandRunner::GetActiveEdges() { + vector edges; + for (map::iterator e = subproc_to_edge_.begin(); + e != subproc_to_edge_.end(); ++e) + edges.push_back(e->second); + return edges; +} + +void RealCommandRunner::Abort() { + subprocs_.Clear(); +} + +bool RealCommandRunner::CanRunMore() { + size_t subproc_number = + subprocs_.running_.size() + subprocs_.finished_.size(); + return (int)subproc_number < config_.parallelism + && ((subprocs_.running_.empty() || config_.max_load_average <= 0.0f) + || GetLoadAverage() < config_.max_load_average); +} + +bool RealCommandRunner::StartCommand(Edge* edge) { + string command = edge->EvaluateCommand(); + Subprocess* subproc = subprocs_.Add(command, edge->use_console()); + if (!subproc) + return false; + subproc_to_edge_.insert(make_pair(subproc, edge)); + + return true; +} + +bool RealCommandRunner::WaitForCommand(Result* result) { + Subprocess* subproc; + while ((subproc = subprocs_.NextFinished()) == NULL) { + bool interrupted = subprocs_.DoWork(); + if (interrupted) + return false; + } + + result->status = subproc->Finish(); + result->output = subproc->GetOutput(); + + map::iterator e = subproc_to_edge_.find(subproc); + result->edge = e->second; + subproc_to_edge_.erase(e); + + delete subproc; + return true; +} + +Builder::Builder(State* state, const BuildConfig& config, + BuildLog* build_log, DepsLog* deps_log, + DiskInterface* disk_interface) + : state_(state), config_(config), disk_interface_(disk_interface), + scan_(state, build_log, deps_log, disk_interface) { + status_ = new BuildStatus(config); +} + +Builder::~Builder() { + Cleanup(); +} + +void Builder::Cleanup() { + if (command_runner_.get()) { + vector active_edges = command_runner_->GetActiveEdges(); + command_runner_->Abort(); + + for (vector::iterator e = active_edges.begin(); + e != active_edges.end(); ++e) { + string depfile = (*e)->GetUnescapedDepfile(); + for (vector::iterator o = (*e)->outputs_.begin(); + o != (*e)->outputs_.end(); ++o) { + // Only delete this output if it was actually modified. This is + // important for things like the generator where we don't want to + // delete the manifest file if we can avoid it. But if the rule + // uses a depfile, always delete. (Consider the case where we + // need to rebuild an output because of a modified header file + // mentioned in a depfile, and the command touches its depfile + // but is interrupted before it touches its output file.) + string err; + TimeStamp new_mtime = disk_interface_->Stat((*o)->path(), &err); + if (new_mtime == -1) // Log and ignore Stat() errors. + Error("%s", err.c_str()); + if (!depfile.empty() || (*o)->mtime() != new_mtime) + disk_interface_->RemoveFile((*o)->path()); + } + if (!depfile.empty()) + disk_interface_->RemoveFile(depfile); + } + } +} + +Node* Builder::AddTarget(const string& name, string* err) { + Node* node = state_->LookupNode(name); + if (!node) { + *err = "unknown target: '" + name + "'"; + return NULL; + } + if (!AddTarget(node, err)) + return NULL; + return node; +} + +bool Builder::AddTarget(Node* node, string* err) { + if (!scan_.RecomputeDirty(node, err)) + return false; + + if (Edge* in_edge = node->in_edge()) { + if (in_edge->outputs_ready()) + return true; // Nothing to do. + } + + if (!plan_.AddTarget(node, err)) + return false; + + return true; +} + +bool Builder::AlreadyUpToDate() const { + return !plan_.more_to_do(); +} + +bool Builder::Build(string* err) { + assert(!AlreadyUpToDate()); + + status_->PlanHasTotalEdges(plan_.command_edge_count()); + int pending_commands = 0; + int failures_allowed = config_.failures_allowed; + + // Set up the command runner if we haven't done so already. + if (!command_runner_.get()) { + if (config_.dry_run) + command_runner_.reset(new DryRunCommandRunner); + else + command_runner_.reset(new RealCommandRunner(config_)); + } + + // We are about to start the build process. + status_->BuildStarted(); + + // This main loop runs the entire build process. + // It is structured like this: + // First, we attempt to start as many commands as allowed by the + // command runner. + // Second, we attempt to wait for / reap the next finished command. + while (plan_.more_to_do()) { + // See if we can start any more commands. + if (failures_allowed && command_runner_->CanRunMore()) { + if (Edge* edge = plan_.FindWork()) { + if (!StartEdge(edge, err)) { + Cleanup(); + status_->BuildFinished(); + return false; + } + + if (edge->is_phony()) { + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + } else { + ++pending_commands; + } + + // We made some progress; go back to the main loop. + continue; + } + } + + // See if we can reap any finished commands. + if (pending_commands) { + CommandRunner::Result result; + if (!command_runner_->WaitForCommand(&result) || + result.status == ExitInterrupted) { + Cleanup(); + status_->BuildFinished(); + *err = "interrupted by user"; + return false; + } + + --pending_commands; + if (!FinishCommand(&result, err)) { + Cleanup(); + status_->BuildFinished(); + return false; + } + + if (!result.success()) { + if (failures_allowed) + failures_allowed--; + } + + // We made some progress; start the main loop over. + continue; + } + + // If we get here, we cannot make any more progress. + status_->BuildFinished(); + if (failures_allowed == 0) { + if (config_.failures_allowed > 1) + *err = "subcommands failed"; + else + *err = "subcommand failed"; + } else if (failures_allowed < config_.failures_allowed) + *err = "cannot make progress due to previous errors"; + else + *err = "stuck [this is a bug]"; + + return false; + } + + status_->BuildFinished(); + return true; +} + +bool Builder::StartEdge(Edge* edge, string* err) { + METRIC_RECORD("StartEdge"); + if (edge->is_phony()) + return true; + + status_->BuildEdgeStarted(edge); + + // Create directories necessary for outputs. + // XXX: this will block; do we care? + for (vector::iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) { + if (!disk_interface_->MakeDirs((*o)->path())) + return false; + } + + // Create response file, if needed + // XXX: this may also block; do we care? + string rspfile = edge->GetUnescapedRspfile(); + if (!rspfile.empty()) { + string content = edge->GetBinding("rspfile_content"); + if (!disk_interface_->WriteFile(rspfile, content)) + return false; + } + + // start command computing and run it + if (!command_runner_->StartCommand(edge)) { + err->assign("command '" + edge->EvaluateCommand() + "' failed."); + return false; + } + + return true; +} + +bool Builder::FinishCommand(CommandRunner::Result* result, string* err) { + METRIC_RECORD("FinishCommand"); + + Edge* edge = result->edge; + + // First try to extract dependencies from the result, if any. + // This must happen first as it filters the command output (we want + // to filter /showIncludes output, even on compile failure) and + // extraction itself can fail, which makes the command fail from a + // build perspective. + vector deps_nodes; + string deps_type = edge->GetBinding("deps"); + const string deps_prefix = edge->GetBinding("msvc_deps_prefix"); + if (!deps_type.empty()) { + string extract_err; + if (!ExtractDeps(result, deps_type, deps_prefix, &deps_nodes, + &extract_err) && + result->success()) { + if (!result->output.empty()) + result->output.append("\n"); + result->output.append(extract_err); + result->status = ExitFailure; + } + } + + int start_time, end_time; + status_->BuildEdgeFinished(edge, result->success(), result->output, + &start_time, &end_time); + + // The rest of this function only applies to successful commands. + if (!result->success()) { + plan_.EdgeFinished(edge, Plan::kEdgeFailed); + return true; + } + + // Restat the edge outputs + TimeStamp output_mtime = 0; + bool restat = edge->GetBindingBool("restat"); + if (!config_.dry_run) { + bool node_cleaned = false; + + for (vector::iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) { + TimeStamp new_mtime = disk_interface_->Stat((*o)->path(), err); + if (new_mtime == -1) + return false; + if (new_mtime > output_mtime) + output_mtime = new_mtime; + if ((*o)->mtime() == new_mtime && restat) { + // The rule command did not change the output. Propagate the clean + // state through the build graph. + // Note that this also applies to nonexistent outputs (mtime == 0). + if (!plan_.CleanNode(&scan_, *o, err)) + return false; + node_cleaned = true; + } + } + + if (node_cleaned) { + TimeStamp restat_mtime = 0; + // If any output was cleaned, find the most recent mtime of any + // (existing) non-order-only input or the depfile. + for (vector::iterator i = edge->inputs_.begin(); + i != edge->inputs_.end() - edge->order_only_deps_; ++i) { + TimeStamp input_mtime = disk_interface_->Stat((*i)->path(), err); + if (input_mtime == -1) + return false; + if (input_mtime > restat_mtime) + restat_mtime = input_mtime; + } + + string depfile = edge->GetUnescapedDepfile(); + if (restat_mtime != 0 && deps_type.empty() && !depfile.empty()) { + TimeStamp depfile_mtime = disk_interface_->Stat(depfile, err); + if (depfile_mtime == -1) + return false; + if (depfile_mtime > restat_mtime) + restat_mtime = depfile_mtime; + } + + // The total number of edges in the plan may have changed as a result + // of a restat. + status_->PlanHasTotalEdges(plan_.command_edge_count()); + + output_mtime = restat_mtime; + } + } + + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + // Delete any left over response file. + string rspfile = edge->GetUnescapedRspfile(); + if (!rspfile.empty() && !g_keep_rsp) + disk_interface_->RemoveFile(rspfile); + + if (scan_.build_log()) { + if (!scan_.build_log()->RecordCommand(edge, start_time, end_time, + output_mtime)) { + *err = string("Error writing to build log: ") + strerror(errno); + return false; + } + } + + if (!deps_type.empty() && !config_.dry_run) { + assert(edge->outputs_.size() == 1 && "should have been rejected by parser"); + Node* out = edge->outputs_[0]; + TimeStamp deps_mtime = disk_interface_->Stat(out->path(), err); + if (deps_mtime == -1) + return false; + if (!scan_.deps_log()->RecordDeps(out, deps_mtime, deps_nodes)) { + *err = string("Error writing to deps log: ") + strerror(errno); + return false; + } + } + return true; +} + +bool Builder::ExtractDeps(CommandRunner::Result* result, + const string& deps_type, + const string& deps_prefix, + vector* deps_nodes, + string* err) { + if (deps_type == "msvc") { + CLParser parser; + string output; + if (!parser.Parse(result->output, deps_prefix, &output, err)) + return false; + result->output = output; + for (set::iterator i = parser.includes_.begin(); + i != parser.includes_.end(); ++i) { + // ~0 is assuming that with MSVC-parsed headers, it's ok to always make + // all backslashes (as some of the slashes will certainly be backslashes + // anyway). This could be fixed if necessary with some additional + // complexity in IncludesNormalize::Relativize. + deps_nodes->push_back(state_->GetNode(*i, ~0u)); + } + } else + if (deps_type == "gcc") { + string depfile = result->edge->GetUnescapedDepfile(); + if (depfile.empty()) { + *err = string("edge with deps=gcc but no depfile makes no sense"); + return false; + } + + // Read depfile content. Treat a missing depfile as empty. + string content; + switch (disk_interface_->ReadFile(depfile, &content, err)) { + case DiskInterface::Okay: + break; + case DiskInterface::NotFound: + err->clear(); + break; + case DiskInterface::OtherError: + return false; + } + if (content.empty()) + return true; + + DepfileParser deps; + if (!deps.Parse(&content, err)) + return false; + + // XXX check depfile matches expected output. + deps_nodes->reserve(deps.ins_.size()); + for (vector::iterator i = deps.ins_.begin(); + i != deps.ins_.end(); ++i) { + uint64_t slash_bits; + if (!CanonicalizePath(const_cast(i->str_), &i->len_, &slash_bits, + err)) + return false; + deps_nodes->push_back(state_->GetNode(*i, slash_bits)); + } + + if (!g_keep_depfile) { + if (disk_interface_->RemoveFile(depfile) < 0) { + *err = string("deleting depfile: ") + strerror(errno) + string("\n"); + return false; + } + } + } else { + Fatal("unknown deps type '%s'", deps_type.c_str()); + } + + return true; +} diff --git a/src/3rdparty/ninja/src/build.h b/src/3rdparty/ninja/src/build.h new file mode 100644 index 00000000000..43786f1c928 --- /dev/null +++ b/src/3rdparty/ninja/src/build.h @@ -0,0 +1,295 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_BUILD_H_ +#define NINJA_BUILD_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "graph.h" // XXX needed for DependencyScan; should rearrange. +#include "exit_status.h" +#include "line_printer.h" +#include "metrics.h" +#include "util.h" // int64_t + +struct BuildLog; +struct BuildStatus; +struct DiskInterface; +struct Edge; +struct Node; +struct State; + +/// Plan stores the state of a build plan: what we intend to build, +/// which steps we're ready to execute. +struct Plan { + Plan(); + + /// Add a target to our plan (including all its dependencies). + /// Returns false if we don't need to build this target; may + /// fill in |err| with an error message if there's a problem. + bool AddTarget(Node* node, string* err); + + // Pop a ready edge off the queue of edges to build. + // Returns NULL if there's no work to do. + Edge* FindWork(); + + /// Returns true if there's more work to be done. + bool more_to_do() const { return wanted_edges_ > 0 && command_edges_ > 0; } + + /// Dumps the current state of the plan. + void Dump(); + + enum EdgeResult { + kEdgeFailed, + kEdgeSucceeded + }; + + /// Mark an edge as done building (whether it succeeded or failed). + void EdgeFinished(Edge* edge, EdgeResult result); + + /// Clean the given node during the build. + /// Return false on error. + bool CleanNode(DependencyScan* scan, Node* node, string* err); + + /// Number of edges with commands to run. + int command_edge_count() const { return command_edges_; } + + /// Reset state. Clears want and ready sets. + void Reset(); + +private: + bool AddSubTarget(Node* node, Node* dependent, string* err); + void NodeFinished(Node* node); + + /// Submits a ready edge as a candidate for execution. + /// The edge may be delayed from running, for example if it's a member of a + /// currently-full pool. + void ScheduleWork(Edge* edge); + + /// Keep track of which edges we want to build in this plan. If this map does + /// not contain an entry for an edge, we do not want to build the entry or its + /// dependents. If an entry maps to false, we do not want to build it, but we + /// might want to build one of its dependents. If the entry maps to true, we + /// want to build it. + map want_; + + set ready_; + + /// Total number of edges that have commands (not phony). + int command_edges_; + + /// Total remaining number of wanted edges. + int wanted_edges_; +}; + +/// CommandRunner is an interface that wraps running the build +/// subcommands. This allows tests to abstract out running commands. +/// RealCommandRunner is an implementation that actually runs commands. +struct CommandRunner { + virtual ~CommandRunner() {} + virtual bool CanRunMore() = 0; + virtual bool StartCommand(Edge* edge) = 0; + + /// The result of waiting for a command. + struct Result { + Result() : edge(NULL) {} + Edge* edge; + ExitStatus status; + string output; + bool success() const { return status == ExitSuccess; } + }; + /// Wait for a command to complete, or return false if interrupted. + virtual bool WaitForCommand(Result* result) = 0; + + virtual vector GetActiveEdges() { return vector(); } + virtual void Abort() {} +}; + +/// Options (e.g. verbosity, parallelism) passed to a build. +struct BuildConfig { + BuildConfig() : verbosity(NORMAL), dry_run(false), parallelism(1), + failures_allowed(1), max_load_average(-0.0f) {} + + enum Verbosity { + NORMAL, + QUIET, // No output -- used when testing. + VERBOSE + }; + Verbosity verbosity; + bool dry_run; + int parallelism; + int failures_allowed; + /// The maximum load average we must not exceed. A negative value + /// means that we do not have any limit. + double max_load_average; +}; + +/// Builder wraps the build process: starting commands, updating status. +struct Builder { + Builder(State* state, const BuildConfig& config, + BuildLog* build_log, DepsLog* deps_log, + DiskInterface* disk_interface); + ~Builder(); + + /// Clean up after interrupted commands by deleting output files. + void Cleanup(); + + Node* AddTarget(const string& name, string* err); + + /// Add a target to the build, scanning dependencies. + /// @return false on error. + bool AddTarget(Node* target, string* err); + + /// Returns true if the build targets are already up to date. + bool AlreadyUpToDate() const; + + /// Run the build. Returns false on error. + /// It is an error to call this function when AlreadyUpToDate() is true. + bool Build(string* err); + + bool StartEdge(Edge* edge, string* err); + + /// Update status ninja logs following a command termination. + /// @return false if the build can not proceed further due to a fatal error. + bool FinishCommand(CommandRunner::Result* result, string* err); + + /// Used for tests. + void SetBuildLog(BuildLog* log) { + scan_.set_build_log(log); + } + + State* state_; + const BuildConfig& config_; + Plan plan_; + auto_ptr command_runner_; + BuildStatus* status_; + + private: + bool ExtractDeps(CommandRunner::Result* result, const string& deps_type, + const string& deps_prefix, vector* deps_nodes, + string* err); + + DiskInterface* disk_interface_; + DependencyScan scan_; + + // Unimplemented copy ctor and operator= ensure we don't copy the auto_ptr. + Builder(const Builder &other); // DO NOT IMPLEMENT + void operator=(const Builder &other); // DO NOT IMPLEMENT +}; + +/// Tracks the status of a build: completion fraction, printing updates. +struct BuildStatus { + explicit BuildStatus(const BuildConfig& config); + void PlanHasTotalEdges(int total); + void BuildEdgeStarted(Edge* edge); + void BuildEdgeFinished(Edge* edge, bool success, const string& output, + int* start_time, int* end_time); + void BuildStarted(); + void BuildFinished(); + + enum EdgeStatus { + kEdgeStarted, + kEdgeFinished, + }; + + /// Format the progress status string by replacing the placeholders. + /// See the user manual for more information about the available + /// placeholders. + /// @param progress_status_format The format of the progress status. + /// @param status The status of the edge. + string FormatProgressStatus(const char* progress_status_format, + EdgeStatus status) const; + + private: + void PrintStatus(Edge* edge, EdgeStatus status); + + const BuildConfig& config_; + + /// Time the build started. + int64_t start_time_millis_; + + int started_edges_, finished_edges_, total_edges_; + + /// Map of running edge to time the edge started running. + typedef map RunningEdgeMap; + RunningEdgeMap running_edges_; + + /// Prints progress output. + LinePrinter printer_; + + /// The custom progress status format to use. + const char* progress_status_format_; + + template + void SnprintfRate(double rate, char(&buf)[S], const char* format) const { + if (rate == -1) + snprintf(buf, S, "?"); + else + snprintf(buf, S, format, rate); + } + + struct RateInfo { + RateInfo() : rate_(-1) {} + + void Restart() { stopwatch_.Restart(); } + double Elapsed() const { return stopwatch_.Elapsed(); } + double rate() { return rate_; } + + void UpdateRate(int edges) { + if (edges && stopwatch_.Elapsed()) + rate_ = edges / stopwatch_.Elapsed(); + } + + private: + double rate_; + Stopwatch stopwatch_; + }; + + struct SlidingRateInfo { + SlidingRateInfo(int n) : rate_(-1), N(n), last_update_(-1) {} + + void Restart() { stopwatch_.Restart(); } + double rate() { return rate_; } + + void UpdateRate(int update_hint) { + if (update_hint == last_update_) + return; + last_update_ = update_hint; + + if (times_.size() == N) + times_.pop(); + times_.push(stopwatch_.Elapsed()); + if (times_.back() != times_.front()) + rate_ = times_.size() / (times_.back() - times_.front()); + } + + private: + double rate_; + Stopwatch stopwatch_; + const size_t N; + queue times_; + int last_update_; + }; + + mutable RateInfo overall_rate_; + mutable SlidingRateInfo current_rate_; +}; + +#endif // NINJA_BUILD_H_ diff --git a/src/3rdparty/ninja/src/build_log.cc b/src/3rdparty/ninja/src/build_log.cc new file mode 100644 index 00000000000..333915af9f9 --- /dev/null +++ b/src/3rdparty/ninja/src/build_log.cc @@ -0,0 +1,408 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// On AIX, inttypes.h gets indirectly included by build_log.h. +// It's easiest just to ask for the printf format macros right away. +#ifndef _WIN32 +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#endif + +#include "build_log.h" + +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "build.h" +#include "graph.h" +#include "metrics.h" +#include "util.h" + +// Implementation details: +// Each run's log appends to the log file. +// To load, we run through all log entries in series, throwing away +// older runs. +// Once the number of redundant entries exceeds a threshold, we write +// out a new file and replace the existing one with it. + +namespace { + +const char kFileSignature[] = "# ninja log v%d\n"; +const int kOldestSupportedVersion = 4; +const int kCurrentVersion = 5; + +// 64bit MurmurHash2, by Austin Appleby +#if defined(_MSC_VER) +#define BIG_CONSTANT(x) (x) +#else // defined(_MSC_VER) +#define BIG_CONSTANT(x) (x##LLU) +#endif // !defined(_MSC_VER) +inline +uint64_t MurmurHash64A(const void* key, size_t len) { + static const uint64_t seed = 0xDECAFBADDECAFBADull; + const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995); + const int r = 47; + uint64_t h = seed ^ (len * m); + const unsigned char* data = (const unsigned char*)key; + while (len >= 8) { + uint64_t k; + memcpy(&k, data, sizeof k); + k *= m; + k ^= k >> r; + k *= m; + h ^= k; + h *= m; + data += 8; + len -= 8; + } + switch (len & 7) + { + case 7: h ^= uint64_t(data[6]) << 48; + case 6: h ^= uint64_t(data[5]) << 40; + case 5: h ^= uint64_t(data[4]) << 32; + case 4: h ^= uint64_t(data[3]) << 24; + case 3: h ^= uint64_t(data[2]) << 16; + case 2: h ^= uint64_t(data[1]) << 8; + case 1: h ^= uint64_t(data[0]); + h *= m; + }; + h ^= h >> r; + h *= m; + h ^= h >> r; + return h; +} +#undef BIG_CONSTANT + + +} // namespace + +// static +uint64_t BuildLog::LogEntry::HashCommand(StringPiece command) { + return MurmurHash64A(command.str_, command.len_); +} + +BuildLog::LogEntry::LogEntry(const string& output) + : output(output) {} + +BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash, + int start_time, int end_time, TimeStamp restat_mtime) + : output(output), command_hash(command_hash), + start_time(start_time), end_time(end_time), mtime(restat_mtime) +{} + +BuildLog::BuildLog() + : log_file_(NULL), needs_recompaction_(false) {} + +BuildLog::~BuildLog() { + Close(); +} + +bool BuildLog::OpenForWrite(const string& path, const BuildLogUser& user, + string* err) { + if (needs_recompaction_) { + if (!Recompact(path, user, err)) + return false; + } + + log_file_ = fopen(path.c_str(), "ab"); + if (!log_file_) { + *err = strerror(errno); + return false; + } + setvbuf(log_file_, NULL, _IOLBF, BUFSIZ); + SetCloseOnExec(fileno(log_file_)); + + // Opening a file in append mode doesn't set the file pointer to the file's + // end on Windows. Do that explicitly. + fseek(log_file_, 0, SEEK_END); + + if (ftell(log_file_) == 0) { + if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) { + *err = strerror(errno); + return false; + } + } + + return true; +} + +bool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time, + TimeStamp mtime) { + string command = edge->EvaluateCommand(true); + uint64_t command_hash = LogEntry::HashCommand(command); + for (vector::iterator out = edge->outputs_.begin(); + out != edge->outputs_.end(); ++out) { + const string& path = (*out)->path(); + Entries::iterator i = entries_.find(path); + LogEntry* log_entry; + if (i != entries_.end()) { + log_entry = i->second; + } else { + log_entry = new LogEntry(path); + entries_.insert(Entries::value_type(log_entry->output, log_entry)); + } + log_entry->command_hash = command_hash; + log_entry->start_time = start_time; + log_entry->end_time = end_time; + log_entry->mtime = mtime; + + if (log_file_) { + if (!WriteEntry(log_file_, *log_entry)) + return false; + } + } + return true; +} + +void BuildLog::Close() { + if (log_file_) + fclose(log_file_); + log_file_ = NULL; +} + +struct LineReader { + explicit LineReader(FILE* file) + : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) { + memset(buf_, 0, sizeof(buf_)); + } + + // Reads a \n-terminated line from the file passed to the constructor. + // On return, *line_start points to the beginning of the next line, and + // *line_end points to the \n at the end of the line. If no newline is seen + // in a fixed buffer size, *line_end is set to NULL. Returns false on EOF. + bool ReadLine(char** line_start, char** line_end) { + if (line_start_ >= buf_end_ || !line_end_) { + // Buffer empty, refill. + size_t size_read = fread(buf_, 1, sizeof(buf_), file_); + if (!size_read) + return false; + line_start_ = buf_; + buf_end_ = buf_ + size_read; + } else { + // Advance to next line in buffer. + line_start_ = line_end_ + 1; + } + + line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_); + if (!line_end_) { + // No newline. Move rest of data to start of buffer, fill rest. + size_t already_consumed = line_start_ - buf_; + size_t size_rest = (buf_end_ - buf_) - already_consumed; + memmove(buf_, line_start_, size_rest); + + size_t read = fread(buf_ + size_rest, 1, sizeof(buf_) - size_rest, file_); + buf_end_ = buf_ + size_rest + read; + line_start_ = buf_; + line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_); + } + + *line_start = line_start_; + *line_end = line_end_; + return true; + } + + private: + FILE* file_; + char buf_[256 << 10]; + char* buf_end_; // Points one past the last valid byte in |buf_|. + + char* line_start_; + // Points at the next \n in buf_ after line_start, or NULL. + char* line_end_; +}; + +bool BuildLog::Load(const string& path, string* err) { + METRIC_RECORD(".ninja_log load"); + FILE* file = fopen(path.c_str(), "r"); + if (!file) { + if (errno == ENOENT) + return true; + *err = strerror(errno); + return false; + } + + int log_version = 0; + int unique_entry_count = 0; + int total_entry_count = 0; + + LineReader reader(file); + char* line_start = 0; + char* line_end = 0; + while (reader.ReadLine(&line_start, &line_end)) { + if (!log_version) { + sscanf(line_start, kFileSignature, &log_version); + + if (log_version < kOldestSupportedVersion) { + *err = ("build log version invalid, perhaps due to being too old; " + "starting over"); + fclose(file); + unlink(path.c_str()); + // Don't report this as a failure. An empty build log will cause + // us to rebuild the outputs anyway. + return true; + } + } + + // If no newline was found in this chunk, read the next. + if (!line_end) + continue; + + const char kFieldSeparator = '\t'; + + char* start = line_start; + char* end = (char*)memchr(start, kFieldSeparator, line_end - start); + if (!end) + continue; + *end = 0; + + int start_time = 0, end_time = 0; + TimeStamp restat_mtime = 0; + + start_time = atoi(start); + start = end + 1; + + end = (char*)memchr(start, kFieldSeparator, line_end - start); + if (!end) + continue; + *end = 0; + end_time = atoi(start); + start = end + 1; + + end = (char*)memchr(start, kFieldSeparator, line_end - start); + if (!end) + continue; + *end = 0; + restat_mtime = atol(start); + start = end + 1; + + end = (char*)memchr(start, kFieldSeparator, line_end - start); + if (!end) + continue; + string output = string(start, end - start); + + start = end + 1; + end = line_end; + + LogEntry* entry; + Entries::iterator i = entries_.find(output); + if (i != entries_.end()) { + entry = i->second; + } else { + entry = new LogEntry(output); + entries_.insert(Entries::value_type(entry->output, entry)); + ++unique_entry_count; + } + ++total_entry_count; + + entry->start_time = start_time; + entry->end_time = end_time; + entry->mtime = restat_mtime; + if (log_version >= 5) { + char c = *end; *end = '\0'; + entry->command_hash = (uint64_t)strtoull(start, NULL, 16); + *end = c; + } else { + entry->command_hash = LogEntry::HashCommand(StringPiece(start, + end - start)); + } + } + fclose(file); + + if (!line_start) { + return true; // file was empty + } + + // Decide whether it's time to rebuild the log: + // - if we're upgrading versions + // - if it's getting large + int kMinCompactionEntryCount = 100; + int kCompactionRatio = 3; + if (log_version < kCurrentVersion) { + needs_recompaction_ = true; + } else if (total_entry_count > kMinCompactionEntryCount && + total_entry_count > unique_entry_count * kCompactionRatio) { + needs_recompaction_ = true; + } + + return true; +} + +BuildLog::LogEntry* BuildLog::LookupByOutput(const string& path) { + Entries::iterator i = entries_.find(path); + if (i != entries_.end()) + return i->second; + return NULL; +} + +bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) { + return fprintf(f, "%d\t%d\t%d\t%s\t%" PRIx64 "\n", + entry.start_time, entry.end_time, entry.mtime, + entry.output.c_str(), entry.command_hash) > 0; +} + +bool BuildLog::Recompact(const string& path, const BuildLogUser& user, + string* err) { + METRIC_RECORD(".ninja_log recompact"); + + Close(); + string temp_path = path + ".recompact"; + FILE* f = fopen(temp_path.c_str(), "wb"); + if (!f) { + *err = strerror(errno); + return false; + } + + if (fprintf(f, kFileSignature, kCurrentVersion) < 0) { + *err = strerror(errno); + fclose(f); + return false; + } + + vector dead_outputs; + for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) { + if (user.IsPathDead(i->first)) { + dead_outputs.push_back(i->first); + continue; + } + + if (!WriteEntry(f, *i->second)) { + *err = strerror(errno); + fclose(f); + return false; + } + } + + for (size_t i = 0; i < dead_outputs.size(); ++i) + entries_.erase(dead_outputs[i]); + + fclose(f); + if (unlink(path.c_str()) < 0) { + *err = strerror(errno); + return false; + } + + if (rename(temp_path.c_str(), path.c_str()) < 0) { + *err = strerror(errno); + return false; + } + + return true; +} diff --git a/src/3rdparty/ninja/src/build_log.h b/src/3rdparty/ninja/src/build_log.h new file mode 100644 index 00000000000..5268fabb684 --- /dev/null +++ b/src/3rdparty/ninja/src/build_log.h @@ -0,0 +1,93 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_BUILD_LOG_H_ +#define NINJA_BUILD_LOG_H_ + +#include +#include +using namespace std; + +#include "hash_map.h" +#include "timestamp.h" +#include "util.h" // uint64_t + +struct Edge; + +/// Can answer questions about the manifest for the BuildLog. +struct BuildLogUser { + /// Return if a given output is no longer part of the build manifest. + /// This is only called during recompaction and doesn't have to be fast. + virtual bool IsPathDead(StringPiece s) const = 0; +}; + +/// Store a log of every command ran for every build. +/// It has a few uses: +/// +/// 1) (hashes of) command lines for existing output files, so we know +/// when we need to rebuild due to the command changing +/// 2) timing information, perhaps for generating reports +/// 3) restat information +struct BuildLog { + BuildLog(); + ~BuildLog(); + + bool OpenForWrite(const string& path, const BuildLogUser& user, string* err); + bool RecordCommand(Edge* edge, int start_time, int end_time, + TimeStamp mtime = 0); + void Close(); + + /// Load the on-disk log. + bool Load(const string& path, string* err); + + struct LogEntry { + string output; + uint64_t command_hash; + int start_time; + int end_time; + TimeStamp mtime; + + static uint64_t HashCommand(StringPiece command); + + // Used by tests. + bool operator==(const LogEntry& o) { + return output == o.output && command_hash == o.command_hash && + start_time == o.start_time && end_time == o.end_time && + mtime == o.mtime; + } + + explicit LogEntry(const string& output); + LogEntry(const string& output, uint64_t command_hash, + int start_time, int end_time, TimeStamp restat_mtime); + }; + + /// Lookup a previously-run command by its output path. + LogEntry* LookupByOutput(const string& path); + + /// Serialize an entry into a log file. + bool WriteEntry(FILE* f, const LogEntry& entry); + + /// Rewrite the known log entries, throwing away old data. + bool Recompact(const string& path, const BuildLogUser& user, string* err); + + typedef ExternalStringHashMap::Type Entries; + const Entries& entries() const { return entries_; } + + private: + Entries entries_; + FILE* log_file_; + bool needs_recompaction_; +}; + +#endif // NINJA_BUILD_LOG_H_ diff --git a/src/3rdparty/ninja/src/build_log_perftest.cc b/src/3rdparty/ninja/src/build_log_perftest.cc new file mode 100644 index 00000000000..e471d138cc1 --- /dev/null +++ b/src/3rdparty/ninja/src/build_log_perftest.cc @@ -0,0 +1,149 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "build_log.h" +#include "graph.h" +#include "manifest_parser.h" +#include "state.h" +#include "util.h" +#include "metrics.h" + +#ifndef _WIN32 +#include +#endif + +const char kTestFilename[] = "BuildLogPerfTest-tempfile"; + +struct NoDeadPaths : public BuildLogUser { + virtual bool IsPathDead(StringPiece) const { return false; } +}; + +bool WriteTestData(string* err) { + BuildLog log; + + NoDeadPaths no_dead_paths; + if (!log.OpenForWrite(kTestFilename, no_dead_paths, err)) + return false; + + /* + A histogram of command lengths in chromium. For example, 407 builds, + 1.4% of all builds, had commands longer than 32 bytes but shorter than 64. + 32 407 1.4% + 64 183 0.6% + 128 1461 5.1% + 256 791 2.8% + 512 1314 4.6% + 1024 6114 21.3% + 2048 11759 41.0% + 4096 2056 7.2% + 8192 4567 15.9% + 16384 13 0.0% + 32768 4 0.0% + 65536 5 0.0% + The average command length is 4.1 kB and there were 28674 commands in total, + which makes for a total log size of ~120 MB (also counting output filenames). + + Based on this, write 30000 many 4 kB long command lines. + */ + + // ManifestParser is the only object allowed to create Rules. + const size_t kRuleSize = 4000; + string long_rule_command = "gcc "; + for (int i = 0; long_rule_command.size() < kRuleSize; ++i) { + char buf[80]; + sprintf(buf, "-I../../and/arbitrary/but/fairly/long/path/suffixed/%d ", i); + long_rule_command += buf; + } + long_rule_command += "$in -o $out\n"; + + State state; + ManifestParser parser(&state, NULL); + if (!parser.ParseTest("rule cxx\n command = " + long_rule_command, err)) + return false; + + // Create build edges. Using ManifestParser is as fast as using the State api + // for edge creation, so just use that. + const int kNumCommands = 30000; + string build_rules; + for (int i = 0; i < kNumCommands; ++i) { + char buf[80]; + sprintf(buf, "build input%d.o: cxx input%d.cc\n", i, i); + build_rules += buf; + } + + if (!parser.ParseTest(build_rules, err)) + return false; + + for (int i = 0; i < kNumCommands; ++i) { + log.RecordCommand(state.edges_[i], + /*start_time=*/100 * i, + /*end_time=*/100 * i + 1, + /*mtime=*/0); + } + + return true; +} + +int main() { + vector times; + string err; + + if (!WriteTestData(&err)) { + fprintf(stderr, "Failed to write test data: %s\n", err.c_str()); + return 1; + } + + { + // Read once to warm up disk cache. + BuildLog log; + if (!log.Load(kTestFilename, &err)) { + fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); + return 1; + } + } + const int kNumRepetitions = 5; + for (int i = 0; i < kNumRepetitions; ++i) { + int64_t start = GetTimeMillis(); + BuildLog log; + if (!log.Load(kTestFilename, &err)) { + fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); + return 1; + } + int delta = (int)(GetTimeMillis() - start); + printf("%dms\n", delta); + times.push_back(delta); + } + + int min = times[0]; + int max = times[0]; + float total = 0; + for (size_t i = 0; i < times.size(); ++i) { + total += times[i]; + if (times[i] < min) + min = times[i]; + else if (times[i] > max) + max = times[i]; + } + + printf("min %dms max %dms avg %.1fms\n", + min, max, total / times.size()); + + unlink(kTestFilename); + + return 0; +} + diff --git a/src/3rdparty/ninja/src/build_log_test.cc b/src/3rdparty/ninja/src/build_log_test.cc new file mode 100644 index 00000000000..ad303805342 --- /dev/null +++ b/src/3rdparty/ninja/src/build_log_test.cc @@ -0,0 +1,307 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "build_log.h" + +#include "util.h" +#include "test.h" + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +namespace { + +const char kTestFilename[] = "BuildLogTest-tempfile"; + +struct BuildLogTest : public StateTestWithBuiltinRules, public BuildLogUser { + virtual void SetUp() { + // In case a crashing test left a stale file behind. + unlink(kTestFilename); + } + virtual void TearDown() { + unlink(kTestFilename); + } + virtual bool IsPathDead(StringPiece s) const { return false; } +}; + +TEST_F(BuildLogTest, WriteRead) { + AssertParse(&state_, +"build out: cat mid\n" +"build mid: cat in\n"); + + BuildLog log1; + string err; + EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); + ASSERT_EQ("", err); + log1.RecordCommand(state_.edges_[0], 15, 18); + log1.RecordCommand(state_.edges_[1], 20, 25); + log1.Close(); + + BuildLog log2; + EXPECT_TRUE(log2.Load(kTestFilename, &err)); + ASSERT_EQ("", err); + + ASSERT_EQ(2u, log1.entries().size()); + ASSERT_EQ(2u, log2.entries().size()); + BuildLog::LogEntry* e1 = log1.LookupByOutput("out"); + ASSERT_TRUE(e1); + BuildLog::LogEntry* e2 = log2.LookupByOutput("out"); + ASSERT_TRUE(e2); + ASSERT_TRUE(*e1 == *e2); + ASSERT_EQ(15, e1->start_time); + ASSERT_EQ("out", e1->output); +} + +TEST_F(BuildLogTest, FirstWriteAddsSignature) { + const char kExpectedVersion[] = "# ninja log vX\n"; + const size_t kVersionPos = strlen(kExpectedVersion) - 2; // Points at 'X'. + + BuildLog log; + string contents, err; + + EXPECT_TRUE(log.OpenForWrite(kTestFilename, *this, &err)); + ASSERT_EQ("", err); + log.Close(); + + ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err)); + ASSERT_EQ("", err); + if (contents.size() >= kVersionPos) + contents[kVersionPos] = 'X'; + EXPECT_EQ(kExpectedVersion, contents); + + // Opening the file anew shouldn't add a second version string. + EXPECT_TRUE(log.OpenForWrite(kTestFilename, *this, &err)); + ASSERT_EQ("", err); + log.Close(); + + contents.clear(); + ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err)); + ASSERT_EQ("", err); + if (contents.size() >= kVersionPos) + contents[kVersionPos] = 'X'; + EXPECT_EQ(kExpectedVersion, contents); +} + +TEST_F(BuildLogTest, DoubleEntry) { + FILE* f = fopen(kTestFilename, "wb"); + fprintf(f, "# ninja log v4\n"); + fprintf(f, "0\t1\t2\tout\tcommand abc\n"); + fprintf(f, "3\t4\t5\tout\tcommand def\n"); + fclose(f); + + string err; + BuildLog log; + EXPECT_TRUE(log.Load(kTestFilename, &err)); + ASSERT_EQ("", err); + + BuildLog::LogEntry* e = log.LookupByOutput("out"); + ASSERT_TRUE(e); + ASSERT_NO_FATAL_FAILURE(AssertHash("command def", e->command_hash)); +} + +TEST_F(BuildLogTest, Truncate) { + AssertParse(&state_, +"build out: cat mid\n" +"build mid: cat in\n"); + + { + BuildLog log1; + string err; + EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); + ASSERT_EQ("", err); + log1.RecordCommand(state_.edges_[0], 15, 18); + log1.RecordCommand(state_.edges_[1], 20, 25); + log1.Close(); + } + + struct stat statbuf; + ASSERT_EQ(0, stat(kTestFilename, &statbuf)); + ASSERT_GT(statbuf.st_size, 0); + + // For all possible truncations of the input file, assert that we don't + // crash when parsing. + for (off_t size = statbuf.st_size; size > 0; --size) { + BuildLog log2; + string err; + EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err)); + ASSERT_EQ("", err); + log2.RecordCommand(state_.edges_[0], 15, 18); + log2.RecordCommand(state_.edges_[1], 20, 25); + log2.Close(); + + ASSERT_TRUE(Truncate(kTestFilename, size, &err)); + + BuildLog log3; + err.clear(); + ASSERT_TRUE(log3.Load(kTestFilename, &err) || !err.empty()); + } +} + +TEST_F(BuildLogTest, ObsoleteOldVersion) { + FILE* f = fopen(kTestFilename, "wb"); + fprintf(f, "# ninja log v3\n"); + fprintf(f, "123 456 0 out command\n"); + fclose(f); + + string err; + BuildLog log; + EXPECT_TRUE(log.Load(kTestFilename, &err)); + ASSERT_NE(err.find("version"), string::npos); +} + +TEST_F(BuildLogTest, SpacesInOutputV4) { + FILE* f = fopen(kTestFilename, "wb"); + fprintf(f, "# ninja log v4\n"); + fprintf(f, "123\t456\t456\tout with space\tcommand\n"); + fclose(f); + + string err; + BuildLog log; + EXPECT_TRUE(log.Load(kTestFilename, &err)); + ASSERT_EQ("", err); + + BuildLog::LogEntry* e = log.LookupByOutput("out with space"); + ASSERT_TRUE(e); + ASSERT_EQ(123, e->start_time); + ASSERT_EQ(456, e->end_time); + ASSERT_EQ(456, e->mtime); + ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash)); +} + +TEST_F(BuildLogTest, DuplicateVersionHeader) { + // Old versions of ninja accidentally wrote multiple version headers to the + // build log on Windows. This shouldn't crash, and the second version header + // should be ignored. + FILE* f = fopen(kTestFilename, "wb"); + fprintf(f, "# ninja log v4\n"); + fprintf(f, "123\t456\t456\tout\tcommand\n"); + fprintf(f, "# ninja log v4\n"); + fprintf(f, "456\t789\t789\tout2\tcommand2\n"); + fclose(f); + + string err; + BuildLog log; + EXPECT_TRUE(log.Load(kTestFilename, &err)); + ASSERT_EQ("", err); + + BuildLog::LogEntry* e = log.LookupByOutput("out"); + ASSERT_TRUE(e); + ASSERT_EQ(123, e->start_time); + ASSERT_EQ(456, e->end_time); + ASSERT_EQ(456, e->mtime); + ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash)); + + e = log.LookupByOutput("out2"); + ASSERT_TRUE(e); + ASSERT_EQ(456, e->start_time); + ASSERT_EQ(789, e->end_time); + ASSERT_EQ(789, e->mtime); + ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash)); +} + +TEST_F(BuildLogTest, VeryLongInputLine) { + // Ninja's build log buffer is currently 256kB. Lines longer than that are + // silently ignored, but don't affect parsing of other lines. + FILE* f = fopen(kTestFilename, "wb"); + fprintf(f, "# ninja log v4\n"); + fprintf(f, "123\t456\t456\tout\tcommand start"); + for (size_t i = 0; i < (512 << 10) / strlen(" more_command"); ++i) + fputs(" more_command", f); + fprintf(f, "\n"); + fprintf(f, "456\t789\t789\tout2\tcommand2\n"); + fclose(f); + + string err; + BuildLog log; + EXPECT_TRUE(log.Load(kTestFilename, &err)); + ASSERT_EQ("", err); + + BuildLog::LogEntry* e = log.LookupByOutput("out"); + ASSERT_EQ(NULL, e); + + e = log.LookupByOutput("out2"); + ASSERT_TRUE(e); + ASSERT_EQ(456, e->start_time); + ASSERT_EQ(789, e->end_time); + ASSERT_EQ(789, e->mtime); + ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash)); +} + +TEST_F(BuildLogTest, MultiTargetEdge) { + AssertParse(&state_, +"build out out.d: cat\n"); + + BuildLog log; + log.RecordCommand(state_.edges_[0], 21, 22); + + ASSERT_EQ(2u, log.entries().size()); + BuildLog::LogEntry* e1 = log.LookupByOutput("out"); + ASSERT_TRUE(e1); + BuildLog::LogEntry* e2 = log.LookupByOutput("out.d"); + ASSERT_TRUE(e2); + ASSERT_EQ("out", e1->output); + ASSERT_EQ("out.d", e2->output); + ASSERT_EQ(21, e1->start_time); + ASSERT_EQ(21, e2->start_time); + ASSERT_EQ(22, e2->end_time); + ASSERT_EQ(22, e2->end_time); +} + +struct BuildLogRecompactTest : public BuildLogTest { + virtual bool IsPathDead(StringPiece s) const { return s == "out2"; } +}; + +TEST_F(BuildLogRecompactTest, Recompact) { + AssertParse(&state_, +"build out: cat in\n" +"build out2: cat in\n"); + + BuildLog log1; + string err; + EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); + ASSERT_EQ("", err); + // Record the same edge several times, to trigger recompaction + // the next time the log is opened. + for (int i = 0; i < 200; ++i) + log1.RecordCommand(state_.edges_[0], 15, 18 + i); + log1.RecordCommand(state_.edges_[1], 21, 22); + log1.Close(); + + // Load... + BuildLog log2; + EXPECT_TRUE(log2.Load(kTestFilename, &err)); + ASSERT_EQ("", err); + ASSERT_EQ(2u, log2.entries().size()); + ASSERT_TRUE(log2.LookupByOutput("out")); + ASSERT_TRUE(log2.LookupByOutput("out2")); + // ...and force a recompaction. + EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err)); + log2.Close(); + + // "out2" is dead, it should've been removed. + BuildLog log3; + EXPECT_TRUE(log2.Load(kTestFilename, &err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, log2.entries().size()); + ASSERT_TRUE(log2.LookupByOutput("out")); + ASSERT_FALSE(log2.LookupByOutput("out2")); +} + +} // anonymous namespace diff --git a/src/3rdparty/ninja/src/build_test.cc b/src/3rdparty/ninja/src/build_test.cc new file mode 100644 index 00000000000..46ab33ef86c --- /dev/null +++ b/src/3rdparty/ninja/src/build_test.cc @@ -0,0 +1,2310 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "build.h" + +#include + +#include "build_log.h" +#include "deps_log.h" +#include "graph.h" +#include "test.h" + +/// Fixture for tests involving Plan. +// Though Plan doesn't use State, it's useful to have one around +// to create Nodes and Edges. +struct PlanTest : public StateTestWithBuiltinRules { + Plan plan_; + + /// Because FindWork does not return Edges in any sort of predictable order, + // provide a means to get available Edges in order and in a format which is + // easy to write tests around. + void FindWorkSorted(deque* ret, int count) { + struct CompareEdgesByOutput { + static bool cmp(const Edge* a, const Edge* b) { + return a->outputs_[0]->path() < b->outputs_[0]->path(); + } + }; + + for (int i = 0; i < count; ++i) { + ASSERT_TRUE(plan_.more_to_do()); + Edge* edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ret->push_back(edge); + } + ASSERT_FALSE(plan_.FindWork()); + sort(ret->begin(), ret->end(), CompareEdgesByOutput::cmp); + } + + void TestPoolWithDepthOne(const char *test_case); +}; + +TEST_F(PlanTest, Basic) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat mid\n" +"build mid: cat in\n")); + GetNode("mid")->MarkDirty(); + GetNode("out")->MarkDirty(); + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("out"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("mid", edge->outputs_[0]->path()); + + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("mid", edge->inputs_[0]->path()); + ASSERT_EQ("out", edge->outputs_[0]->path()); + + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + ASSERT_FALSE(plan_.more_to_do()); + edge = plan_.FindWork(); + ASSERT_EQ(0, edge); +} + +// Test that two outputs from one rule can be handled as inputs to the next. +TEST_F(PlanTest, DoubleOutputDirect) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat mid1 mid2\n" +"build mid1 mid2: cat in\n")); + GetNode("mid1")->MarkDirty(); + GetNode("mid2")->MarkDirty(); + GetNode("out")->MarkDirty(); + + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("out"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge; + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat in + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat mid1 mid2 + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_FALSE(edge); // done +} + +// Test that two outputs from one rule can eventually be routed to another. +TEST_F(PlanTest, DoubleOutputIndirect) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat b1 b2\n" +"build b1: cat a1\n" +"build b2: cat a2\n" +"build a1 a2: cat in\n")); + GetNode("a1")->MarkDirty(); + GetNode("a2")->MarkDirty(); + GetNode("b1")->MarkDirty(); + GetNode("b2")->MarkDirty(); + GetNode("out")->MarkDirty(); + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("out"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge; + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat in + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat a1 + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat a2 + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat b1 b2 + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_FALSE(edge); // done +} + +// Test that two edges from one output can both execute. +TEST_F(PlanTest, DoubleDependent) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat a1 a2\n" +"build a1: cat mid\n" +"build a2: cat mid\n" +"build mid: cat in\n")); + GetNode("mid")->MarkDirty(); + GetNode("a1")->MarkDirty(); + GetNode("a2")->MarkDirty(); + GetNode("out")->MarkDirty(); + + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("out"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge; + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat in + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat mid + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat mid + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); // cat a1 a2 + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_FALSE(edge); // done +} + +void PlanTest::TestPoolWithDepthOne(const char* test_case) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, test_case)); + GetNode("out1")->MarkDirty(); + GetNode("out2")->MarkDirty(); + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("out1"), &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(plan_.AddTarget(GetNode("out2"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("out1", edge->outputs_[0]->path()); + + // This will be false since poolcat is serialized + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("out2", edge->outputs_[0]->path()); + + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + ASSERT_FALSE(plan_.more_to_do()); + edge = plan_.FindWork(); + ASSERT_EQ(0, edge); +} + +TEST_F(PlanTest, PoolWithDepthOne) { + TestPoolWithDepthOne( +"pool foobar\n" +" depth = 1\n" +"rule poolcat\n" +" command = cat $in > $out\n" +" pool = foobar\n" +"build out1: poolcat in\n" +"build out2: poolcat in\n"); +} + +TEST_F(PlanTest, ConsolePool) { + TestPoolWithDepthOne( +"rule poolcat\n" +" command = cat $in > $out\n" +" pool = console\n" +"build out1: poolcat in\n" +"build out2: poolcat in\n"); +} + +TEST_F(PlanTest, PoolsWithDepthTwo) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"pool foobar\n" +" depth = 2\n" +"pool bazbin\n" +" depth = 2\n" +"rule foocat\n" +" command = cat $in > $out\n" +" pool = foobar\n" +"rule bazcat\n" +" command = cat $in > $out\n" +" pool = bazbin\n" +"build out1: foocat in\n" +"build out2: foocat in\n" +"build out3: foocat in\n" +"build outb1: bazcat in\n" +"build outb2: bazcat in\n" +"build outb3: bazcat in\n" +" pool =\n" +"build allTheThings: cat out1 out2 out3 outb1 outb2 outb3\n" +)); + // Mark all the out* nodes dirty + for (int i = 0; i < 3; ++i) { + GetNode("out" + string(1, '1' + static_cast(i)))->MarkDirty(); + GetNode("outb" + string(1, '1' + static_cast(i)))->MarkDirty(); + } + GetNode("allTheThings")->MarkDirty(); + + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("allTheThings"), &err)); + ASSERT_EQ("", err); + + deque edges; + FindWorkSorted(&edges, 5); + + for (int i = 0; i < 4; ++i) { + Edge *edge = edges[i]; + ASSERT_EQ("in", edge->inputs_[0]->path()); + string base_name(i < 2 ? "out" : "outb"); + ASSERT_EQ(base_name + string(1, '1' + (i % 2)), edge->outputs_[0]->path()); + } + + // outb3 is exempt because it has an empty pool + Edge* edge = edges[4]; + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("outb3", edge->outputs_[0]->path()); + + // finish out1 + plan_.EdgeFinished(edges.front(), Plan::kEdgeSucceeded); + edges.pop_front(); + + // out3 should be available + Edge* out3 = plan_.FindWork(); + ASSERT_TRUE(out3); + ASSERT_EQ("in", out3->inputs_[0]->path()); + ASSERT_EQ("out3", out3->outputs_[0]->path()); + + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(out3, Plan::kEdgeSucceeded); + + ASSERT_FALSE(plan_.FindWork()); + + for (deque::iterator it = edges.begin(); it != edges.end(); ++it) { + plan_.EdgeFinished(*it, Plan::kEdgeSucceeded); + } + + Edge* last = plan_.FindWork(); + ASSERT_TRUE(last); + ASSERT_EQ("allTheThings", last->outputs_[0]->path()); + + plan_.EdgeFinished(last, Plan::kEdgeSucceeded); + + ASSERT_FALSE(plan_.more_to_do()); + ASSERT_FALSE(plan_.FindWork()); +} + +TEST_F(PlanTest, PoolWithRedundantEdges) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "pool compile\n" + " depth = 1\n" + "rule gen_foo\n" + " command = touch foo.cpp\n" + "rule gen_bar\n" + " command = touch bar.cpp\n" + "rule echo\n" + " command = echo $out > $out\n" + "build foo.cpp.obj: echo foo.cpp || foo.cpp\n" + " pool = compile\n" + "build bar.cpp.obj: echo bar.cpp || bar.cpp\n" + " pool = compile\n" + "build libfoo.a: echo foo.cpp.obj bar.cpp.obj\n" + "build foo.cpp: gen_foo\n" + "build bar.cpp: gen_bar\n" + "build all: phony libfoo.a\n")); + GetNode("foo.cpp")->MarkDirty(); + GetNode("foo.cpp.obj")->MarkDirty(); + GetNode("bar.cpp")->MarkDirty(); + GetNode("bar.cpp.obj")->MarkDirty(); + GetNode("libfoo.a")->MarkDirty(); + GetNode("all")->MarkDirty(); + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("all"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge = NULL; + + deque initial_edges; + FindWorkSorted(&initial_edges, 2); + + edge = initial_edges[1]; // Foo first + ASSERT_EQ("foo.cpp", edge->outputs_[0]->path()); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_FALSE(plan_.FindWork()); + ASSERT_EQ("foo.cpp", edge->inputs_[0]->path()); + ASSERT_EQ("foo.cpp", edge->inputs_[1]->path()); + ASSERT_EQ("foo.cpp.obj", edge->outputs_[0]->path()); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = initial_edges[0]; // Now for bar + ASSERT_EQ("bar.cpp", edge->outputs_[0]->path()); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_FALSE(plan_.FindWork()); + ASSERT_EQ("bar.cpp", edge->inputs_[0]->path()); + ASSERT_EQ("bar.cpp", edge->inputs_[1]->path()); + ASSERT_EQ("bar.cpp.obj", edge->outputs_[0]->path()); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_FALSE(plan_.FindWork()); + ASSERT_EQ("foo.cpp.obj", edge->inputs_[0]->path()); + ASSERT_EQ("bar.cpp.obj", edge->inputs_[1]->path()); + ASSERT_EQ("libfoo.a", edge->outputs_[0]->path()); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_FALSE(plan_.FindWork()); + ASSERT_EQ("libfoo.a", edge->inputs_[0]->path()); + ASSERT_EQ("all", edge->outputs_[0]->path()); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); + + edge = plan_.FindWork(); + ASSERT_FALSE(edge); + ASSERT_FALSE(plan_.more_to_do()); +} + +TEST_F(PlanTest, PoolWithFailingEdge) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "pool foobar\n" + " depth = 1\n" + "rule poolcat\n" + " command = cat $in > $out\n" + " pool = foobar\n" + "build out1: poolcat in\n" + "build out2: poolcat in\n")); + GetNode("out1")->MarkDirty(); + GetNode("out2")->MarkDirty(); + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("out1"), &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(plan_.AddTarget(GetNode("out2"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("out1", edge->outputs_[0]->path()); + + // This will be false since poolcat is serialized + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(edge, Plan::kEdgeFailed); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("out2", edge->outputs_[0]->path()); + + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(edge, Plan::kEdgeFailed); + + ASSERT_TRUE(plan_.more_to_do()); // Jobs have failed + edge = plan_.FindWork(); + ASSERT_EQ(0, edge); +} + +/// Fake implementation of CommandRunner, useful for tests. +struct FakeCommandRunner : public CommandRunner { + explicit FakeCommandRunner(VirtualFileSystem* fs) : + last_command_(NULL), fs_(fs) {} + + // CommandRunner impl + virtual bool CanRunMore(); + virtual bool StartCommand(Edge* edge); + virtual bool WaitForCommand(Result* result); + virtual vector GetActiveEdges(); + virtual void Abort(); + + vector commands_ran_; + Edge* last_command_; + VirtualFileSystem* fs_; +}; + +struct BuildTest : public StateTestWithBuiltinRules, public BuildLogUser { + BuildTest() : config_(MakeConfig()), command_runner_(&fs_), + builder_(&state_, config_, NULL, NULL, &fs_), + status_(config_) { + } + + virtual void SetUp() { + StateTestWithBuiltinRules::SetUp(); + + builder_.command_runner_.reset(&command_runner_); + AssertParse(&state_, +"build cat1: cat in1\n" +"build cat2: cat in1 in2\n" +"build cat12: cat cat1 cat2\n"); + + fs_.Create("in1", ""); + fs_.Create("in2", ""); + } + + ~BuildTest() { + builder_.command_runner_.release(); + } + + virtual bool IsPathDead(StringPiece s) const { return false; } + + /// Rebuild target in the 'working tree' (fs_). + /// State of command_runner_ and logs contents (if specified) ARE MODIFIED. + /// Handy to check for NOOP builds, and higher-level rebuild tests. + void RebuildTarget(const string& target, const char* manifest, + const char* log_path = NULL, const char* deps_path = NULL, + State* state = NULL); + + // Mark a path dirty. + void Dirty(const string& path); + + BuildConfig MakeConfig() { + BuildConfig config; + config.verbosity = BuildConfig::QUIET; + return config; + } + + BuildConfig config_; + FakeCommandRunner command_runner_; + VirtualFileSystem fs_; + Builder builder_; + + BuildStatus status_; +}; + +void BuildTest::RebuildTarget(const string& target, const char* manifest, + const char* log_path, const char* deps_path, + State* state) { + State local_state, *pstate = &local_state; + if (state) + pstate = state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(pstate)); + AssertParse(pstate, manifest); + + string err; + BuildLog build_log, *pbuild_log = NULL; + if (log_path) { + ASSERT_TRUE(build_log.Load(log_path, &err)); + ASSERT_TRUE(build_log.OpenForWrite(log_path, *this, &err)); + ASSERT_EQ("", err); + pbuild_log = &build_log; + } + + DepsLog deps_log, *pdeps_log = NULL; + if (deps_path) { + ASSERT_TRUE(deps_log.Load(deps_path, pstate, &err)); + ASSERT_TRUE(deps_log.OpenForWrite(deps_path, &err)); + ASSERT_EQ("", err); + pdeps_log = &deps_log; + } + + Builder builder(pstate, config_, pbuild_log, pdeps_log, &fs_); + EXPECT_TRUE(builder.AddTarget(target, &err)); + + command_runner_.commands_ran_.clear(); + builder.command_runner_.reset(&command_runner_); + if (!builder.AlreadyUpToDate()) { + bool build_res = builder.Build(&err); + EXPECT_TRUE(build_res); + } + builder.command_runner_.release(); +} + +bool FakeCommandRunner::CanRunMore() { + // Only run one at a time. + return last_command_ == NULL; +} + +bool FakeCommandRunner::StartCommand(Edge* edge) { + assert(!last_command_); + commands_ran_.push_back(edge->EvaluateCommand()); + if (edge->rule().name() == "cat" || + edge->rule().name() == "cat_rsp" || + edge->rule().name() == "cat_rsp_out" || + edge->rule().name() == "cc" || + edge->rule().name() == "touch" || + edge->rule().name() == "touch-interrupt" || + edge->rule().name() == "touch-fail-tick2") { + for (vector::iterator out = edge->outputs_.begin(); + out != edge->outputs_.end(); ++out) { + fs_->Create((*out)->path(), ""); + } + } else if (edge->rule().name() == "true" || + edge->rule().name() == "fail" || + edge->rule().name() == "interrupt" || + edge->rule().name() == "console") { + // Don't do anything. + } else { + printf("unknown command\n"); + return false; + } + + last_command_ = edge; + return true; +} + +bool FakeCommandRunner::WaitForCommand(Result* result) { + if (!last_command_) + return false; + + Edge* edge = last_command_; + result->edge = edge; + + if (edge->rule().name() == "interrupt" || + edge->rule().name() == "touch-interrupt") { + result->status = ExitInterrupted; + return true; + } + + if (edge->rule().name() == "console") { + if (edge->use_console()) + result->status = ExitSuccess; + else + result->status = ExitFailure; + last_command_ = NULL; + return true; + } + + if (edge->rule().name() == "fail" || + (edge->rule().name() == "touch-fail-tick2" && fs_->now_ == 2)) + result->status = ExitFailure; + else + result->status = ExitSuccess; + last_command_ = NULL; + return true; +} + +vector FakeCommandRunner::GetActiveEdges() { + vector edges; + if (last_command_) + edges.push_back(last_command_); + return edges; +} + +void FakeCommandRunner::Abort() { + last_command_ = NULL; +} + +void BuildTest::Dirty(const string& path) { + Node* node = GetNode(path); + node->MarkDirty(); + + // If it's an input file, mark that we've already stat()ed it and + // it's missing. + if (!node->in_edge()) + node->MarkMissing(); +} + +TEST_F(BuildTest, NoWork) { + string err; + EXPECT_TRUE(builder_.AlreadyUpToDate()); +} + +TEST_F(BuildTest, OneStep) { + // Given a dirty target with one ready input, + // we should rebuild the target. + Dirty("cat1"); + string err; + EXPECT_TRUE(builder_.AddTarget("cat1", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + EXPECT_EQ("cat in1 > cat1", command_runner_.commands_ran_[0]); +} + +TEST_F(BuildTest, OneStep2) { + // Given a target with one dirty input, + // we should rebuild the target. + Dirty("cat1"); + string err; + EXPECT_TRUE(builder_.AddTarget("cat1", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + EXPECT_EQ("cat in1 > cat1", command_runner_.commands_ran_[0]); +} + +TEST_F(BuildTest, TwoStep) { + string err; + EXPECT_TRUE(builder_.AddTarget("cat12", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + // Depending on how the pointers work out, we could've ran + // the first two commands in either order. + EXPECT_TRUE((command_runner_.commands_ran_[0] == "cat in1 > cat1" && + command_runner_.commands_ran_[1] == "cat in1 in2 > cat2") || + (command_runner_.commands_ran_[1] == "cat in1 > cat1" && + command_runner_.commands_ran_[0] == "cat in1 in2 > cat2")); + + EXPECT_EQ("cat cat1 cat2 > cat12", command_runner_.commands_ran_[2]); + + fs_.Tick(); + + // Modifying in2 requires rebuilding one intermediate file + // and the final file. + fs_.Create("in2", ""); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("cat12", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(5u, command_runner_.commands_ran_.size()); + EXPECT_EQ("cat in1 in2 > cat2", command_runner_.commands_ran_[3]); + EXPECT_EQ("cat cat1 cat2 > cat12", command_runner_.commands_ran_[4]); +} + +TEST_F(BuildTest, TwoOutputs) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch $out\n" +"build out1 out2: touch in.txt\n")); + + fs_.Create("in.txt", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + EXPECT_EQ("touch out1 out2", command_runner_.commands_ran_[0]); +} + +TEST_F(BuildTest, ImplicitOutput) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch $out $out.imp\n" +"build out | out.imp: touch in.txt\n")); + fs_.Create("in.txt", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out.imp", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + EXPECT_EQ("touch out out.imp", command_runner_.commands_ran_[0]); +} + +// Test case from +// https://github.com/ninja-build/ninja/issues/148 +TEST_F(BuildTest, MultiOutIn) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch $out\n" +"build in1 otherfile: touch in\n" +"build out: touch in | in1\n")); + + fs_.Create("in", ""); + fs_.Tick(); + fs_.Create("in1", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); +} + +TEST_F(BuildTest, Chain) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build c2: cat c1\n" +"build c3: cat c2\n" +"build c4: cat c3\n" +"build c5: cat c4\n")); + + fs_.Create("c1", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("c5", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(4u, command_runner_.commands_ran_.size()); + + err.clear(); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("c5", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); + + fs_.Tick(); + + fs_.Create("c3", ""); + err.clear(); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("c5", &err)); + ASSERT_EQ("", err); + EXPECT_FALSE(builder_.AlreadyUpToDate()); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); // 3->4, 4->5 +} + +TEST_F(BuildTest, MissingInput) { + // Input is referenced by build file, but no rule for it. + string err; + Dirty("in1"); + EXPECT_FALSE(builder_.AddTarget("cat1", &err)); + EXPECT_EQ("'in1', needed by 'cat1', missing and no known rule to make it", + err); +} + +TEST_F(BuildTest, MissingTarget) { + // Target is not referenced by build file. + string err; + EXPECT_FALSE(builder_.AddTarget("meow", &err)); + EXPECT_EQ("unknown target: 'meow'", err); +} + +TEST_F(BuildTest, MakeDirs) { + string err; + +#ifdef _WIN32 + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "build subdir\\dir2\\file: cat in1\n")); +#else + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "build subdir/dir2/file: cat in1\n")); +#endif + EXPECT_TRUE(builder_.AddTarget("subdir/dir2/file", &err)); + + EXPECT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(2u, fs_.directories_made_.size()); + EXPECT_EQ("subdir", fs_.directories_made_[0]); + EXPECT_EQ("subdir/dir2", fs_.directories_made_[1]); +} + +TEST_F(BuildTest, DepFileMissing) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n command = cc $in\n depfile = $out.d\n" +"build fo$ o.o: cc foo.c\n")); + fs_.Create("foo.c", ""); + + EXPECT_TRUE(builder_.AddTarget("fo o.o", &err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, fs_.files_read_.size()); + EXPECT_EQ("fo o.o.d", fs_.files_read_[0]); +} + +TEST_F(BuildTest, DepFileOK) { + string err; + int orig_edges = state_.edges_.size(); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n command = cc $in\n depfile = $out.d\n" +"build foo.o: cc foo.c\n")); + Edge* edge = state_.edges_.back(); + + fs_.Create("foo.c", ""); + GetNode("bar.h")->MarkDirty(); // Mark bar.h as missing. + fs_.Create("foo.o.d", "foo.o: blah.h bar.h\n"); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, fs_.files_read_.size()); + EXPECT_EQ("foo.o.d", fs_.files_read_[0]); + + // Expect three new edges: one generating foo.o, and two more from + // loading the depfile. + ASSERT_EQ(orig_edges + 3, (int)state_.edges_.size()); + // Expect our edge to now have three inputs: foo.c and two headers. + ASSERT_EQ(3u, edge->inputs_.size()); + + // Expect the command line we generate to only use the original input. + ASSERT_EQ("cc foo.c", edge->EvaluateCommand()); +} + +TEST_F(BuildTest, DepFileParseError) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n command = cc $in\n depfile = $out.d\n" +"build foo.o: cc foo.c\n")); + fs_.Create("foo.c", ""); + fs_.Create("foo.o.d", "randomtext\n"); + EXPECT_FALSE(builder_.AddTarget("foo.o", &err)); + EXPECT_EQ("foo.o.d: expected ':' in depfile", err); +} + +TEST_F(BuildTest, EncounterReadyTwice) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch $out\n" +"build c: touch\n" +"build b: touch || c\n" +"build a: touch | b || c\n")); + + vector c_out = GetNode("c")->out_edges(); + ASSERT_EQ(2u, c_out.size()); + EXPECT_EQ("b", c_out[0]->outputs_[0]->path()); + EXPECT_EQ("a", c_out[1]->outputs_[0]->path()); + + fs_.Create("b", ""); + EXPECT_TRUE(builder_.AddTarget("a", &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, OrderOnlyDeps) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n command = cc $in\n depfile = $out.d\n" +"build foo.o: cc foo.c || otherfile\n")); + Edge* edge = state_.edges_.back(); + + fs_.Create("foo.c", ""); + fs_.Create("otherfile", ""); + fs_.Create("foo.o.d", "foo.o: blah.h bar.h\n"); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + ASSERT_EQ("", err); + + // One explicit, two implicit, one order only. + ASSERT_EQ(4u, edge->inputs_.size()); + EXPECT_EQ(2, edge->implicit_deps_); + EXPECT_EQ(1, edge->order_only_deps_); + // Verify the inputs are in the order we expect + // (explicit then implicit then orderonly). + EXPECT_EQ("foo.c", edge->inputs_[0]->path()); + EXPECT_EQ("blah.h", edge->inputs_[1]->path()); + EXPECT_EQ("bar.h", edge->inputs_[2]->path()); + EXPECT_EQ("otherfile", edge->inputs_[3]->path()); + + // Expect the command line we generate to only use the original input. + ASSERT_EQ("cc foo.c", edge->EvaluateCommand()); + + // explicit dep dirty, expect a rebuild. + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + + fs_.Tick(); + + // Recreate the depfile, as it should have been deleted by the build. + fs_.Create("foo.o.d", "foo.o: blah.h bar.h\n"); + + // implicit dep dirty, expect a rebuild. + fs_.Create("blah.h", ""); + fs_.Create("bar.h", ""); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + + fs_.Tick(); + + // Recreate the depfile, as it should have been deleted by the build. + fs_.Create("foo.o.d", "foo.o: blah.h bar.h\n"); + + // order only dep dirty, no rebuild. + fs_.Create("otherfile", ""); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + EXPECT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); + + // implicit dep missing, expect rebuild. + fs_.RemoveFile("bar.h"); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, RebuildOrderOnlyDeps) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n command = cc $in\n" +"rule true\n command = true\n" +"build oo.h: cc oo.h.in\n" +"build foo.o: cc foo.c || oo.h\n")); + + fs_.Create("foo.c", ""); + fs_.Create("oo.h.in", ""); + + // foo.o and order-only dep dirty, build both. + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); + + // all clean, no rebuild. + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + EXPECT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); + + // order-only dep missing, build it only. + fs_.RemoveFile("oo.h"); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + ASSERT_EQ("cc oo.h.in", command_runner_.commands_ran_[0]); + + fs_.Tick(); + + // order-only dep dirty, build it only. + fs_.Create("oo.h.in", ""); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + ASSERT_EQ("cc oo.h.in", command_runner_.commands_ran_[0]); +} + +#ifdef _WIN32 +TEST_F(BuildTest, DepFileCanonicalize) { + string err; + int orig_edges = state_.edges_.size(); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n command = cc $in\n depfile = $out.d\n" +"build gen/stuff\\things/foo.o: cc x\\y/z\\foo.c\n")); + Edge* edge = state_.edges_.back(); + + fs_.Create("x/y/z/foo.c", ""); + GetNode("bar.h")->MarkDirty(); // Mark bar.h as missing. + // Note, different slashes from manifest. + fs_.Create("gen/stuff\\things/foo.o.d", + "gen\\stuff\\things\\foo.o: blah.h bar.h\n"); + EXPECT_TRUE(builder_.AddTarget("gen/stuff/things/foo.o", &err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, fs_.files_read_.size()); + // The depfile path does not get Canonicalize as it seems unnecessary. + EXPECT_EQ("gen/stuff\\things/foo.o.d", fs_.files_read_[0]); + + // Expect three new edges: one generating foo.o, and two more from + // loading the depfile. + ASSERT_EQ(orig_edges + 3, (int)state_.edges_.size()); + // Expect our edge to now have three inputs: foo.c and two headers. + ASSERT_EQ(3u, edge->inputs_.size()); + + // Expect the command line we generate to only use the original input, and + // using the slashes from the manifest. + ASSERT_EQ("cc x\\y/z\\foo.c", edge->EvaluateCommand()); +} +#endif + +TEST_F(BuildTest, Phony) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat bar.cc\n" +"build all: phony out\n")); + fs_.Create("bar.cc", ""); + + EXPECT_TRUE(builder_.AddTarget("all", &err)); + ASSERT_EQ("", err); + + // Only one command to run, because phony runs no command. + EXPECT_FALSE(builder_.AlreadyUpToDate()); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, PhonyNoWork) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat bar.cc\n" +"build all: phony out\n")); + fs_.Create("bar.cc", ""); + fs_.Create("out", ""); + + EXPECT_TRUE(builder_.AddTarget("all", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); +} + +// Test a self-referencing phony. Ideally this should not work, but +// ninja 1.7 and below tolerated and CMake 2.8.12.x and 3.0.x both +// incorrectly produce it. We tolerate it for compatibility. +TEST_F(BuildTest, PhonySelfReference) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build a: phony a\n")); + + EXPECT_TRUE(builder_.AddTarget("a", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); +} + +TEST_F(BuildTest, Fail) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule fail\n" +" command = fail\n" +"build out1: fail\n")); + + string err; + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + ASSERT_EQ("", err); + + EXPECT_FALSE(builder_.Build(&err)); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + ASSERT_EQ("subcommand failed", err); +} + +TEST_F(BuildTest, SwallowFailures) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule fail\n" +" command = fail\n" +"build out1: fail\n" +"build out2: fail\n" +"build out3: fail\n" +"build all: phony out1 out2 out3\n")); + + // Swallow two failures, die on the third. + config_.failures_allowed = 3; + + string err; + EXPECT_TRUE(builder_.AddTarget("all", &err)); + ASSERT_EQ("", err); + + EXPECT_FALSE(builder_.Build(&err)); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + ASSERT_EQ("subcommands failed", err); +} + +TEST_F(BuildTest, SwallowFailuresLimit) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule fail\n" +" command = fail\n" +"build out1: fail\n" +"build out2: fail\n" +"build out3: fail\n" +"build final: cat out1 out2 out3\n")); + + // Swallow ten failures; we should stop before building final. + config_.failures_allowed = 11; + + string err; + EXPECT_TRUE(builder_.AddTarget("final", &err)); + ASSERT_EQ("", err); + + EXPECT_FALSE(builder_.Build(&err)); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + ASSERT_EQ("cannot make progress due to previous errors", err); +} + +TEST_F(BuildTest, SwallowFailuresPool) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"pool failpool\n" +" depth = 1\n" +"rule fail\n" +" command = fail\n" +" pool = failpool\n" +"build out1: fail\n" +"build out2: fail\n" +"build out3: fail\n" +"build final: cat out1 out2 out3\n")); + + // Swallow ten failures; we should stop before building final. + config_.failures_allowed = 11; + + string err; + EXPECT_TRUE(builder_.AddTarget("final", &err)); + ASSERT_EQ("", err); + + EXPECT_FALSE(builder_.Build(&err)); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + ASSERT_EQ("cannot make progress due to previous errors", err); +} + +TEST_F(BuildTest, PoolEdgesReadyButNotWanted) { + fs_.Create("x", ""); + + const char* manifest = + "pool some_pool\n" + " depth = 4\n" + "rule touch\n" + " command = touch $out\n" + " pool = some_pool\n" + "rule cc\n" + " command = touch grit\n" + "\n" + "build B.d.stamp: cc | x\n" + "build C.stamp: touch B.d.stamp\n" + "build final.stamp: touch || C.stamp\n"; + + RebuildTarget("final.stamp", manifest); + + fs_.RemoveFile("B.d.stamp"); + + State save_state; + RebuildTarget("final.stamp", manifest, NULL, NULL, &save_state); + EXPECT_GE(save_state.LookupPool("some_pool")->current_use(), 0); +} + +struct BuildWithLogTest : public BuildTest { + BuildWithLogTest() { + builder_.SetBuildLog(&build_log_); + } + + BuildLog build_log_; +}; + +TEST_F(BuildWithLogTest, NotInLogButOnDisk) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n" +" command = cc\n" +"build out1: cc in\n")); + + // Create input/output that would be considered up to date when + // not considering the command line hash. + fs_.Create("in", ""); + fs_.Create("out1", ""); + string err; + + // Because it's not in the log, it should not be up-to-date until + // we build again. + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_FALSE(builder_.AlreadyUpToDate()); + + command_runner_.commands_ran_.clear(); + state_.Reset(); + + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_TRUE(builder_.AlreadyUpToDate()); +} + +TEST_F(BuildWithLogTest, RebuildAfterFailure) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch-fail-tick2\n" +" command = touch-fail-tick2\n" +"build out1: touch-fail-tick2 in\n")); + + string err; + + fs_.Create("in", ""); + + // Run once successfully to get out1 in the log + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); + + command_runner_.commands_ran_.clear(); + state_.Reset(); + builder_.Cleanup(); + builder_.plan_.Reset(); + + fs_.Tick(); + fs_.Create("in", ""); + + // Run again with a failure that updates the output file timestamp + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ("subcommand failed", err); + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); + + command_runner_.commands_ran_.clear(); + state_.Reset(); + builder_.Cleanup(); + builder_.plan_.Reset(); + + fs_.Tick(); + + // Run again, should rerun even though the output file is up to date on disk + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_FALSE(builder_.AlreadyUpToDate()); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); + EXPECT_EQ("", err); +} + +TEST_F(BuildWithLogTest, RebuildWithNoInputs) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch\n" +"build out1: touch\n" +"build out2: touch in\n")); + + string err; + + fs_.Create("in", ""); + + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + EXPECT_EQ(2u, command_runner_.commands_ran_.size()); + + command_runner_.commands_ran_.clear(); + state_.Reset(); + + fs_.Tick(); + + fs_.Create("in", ""); + + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildWithLogTest, RestatTest) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule true\n" +" command = true\n" +" restat = 1\n" +"rule cc\n" +" command = cc\n" +" restat = 1\n" +"build out1: cc in\n" +"build out2: true out1\n" +"build out3: cat out2\n")); + + fs_.Create("out1", ""); + fs_.Create("out2", ""); + fs_.Create("out3", ""); + + fs_.Tick(); + + fs_.Create("in", ""); + + // Do a pre-build so that there's commands in the log for the outputs, + // otherwise, the lack of an entry in the build log will cause out3 to rebuild + // regardless of restat. + string err; + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + EXPECT_EQ("[3/3]", builder_.status_->FormatProgressStatus("[%s/%t]", + BuildStatus::kEdgeStarted)); + command_runner_.commands_ran_.clear(); + state_.Reset(); + + fs_.Tick(); + + fs_.Create("in", ""); + // "cc" touches out1, so we should build out2. But because "true" does not + // touch out2, we should cancel the build of out3. + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); + + // If we run again, it should be a no-op, because the build log has recorded + // that we've already built out2 with an input timestamp of 2 (from out1). + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); + + fs_.Tick(); + + fs_.Create("in", ""); + + // The build log entry should not, however, prevent us from rebuilding out2 + // if out1 changes. + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildWithLogTest, RestatMissingFile) { + // If a restat rule doesn't create its output, and the output didn't + // exist before the rule was run, consider that behavior equivalent + // to a rule that doesn't modify its existent output file. + + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule true\n" +" command = true\n" +" restat = 1\n" +"rule cc\n" +" command = cc\n" +"build out1: true in\n" +"build out2: cc out1\n")); + + fs_.Create("in", ""); + fs_.Create("out2", ""); + + // Do a pre-build so that there's commands in the log for the outputs, + // otherwise, the lack of an entry in the build log will cause out2 to rebuild + // regardless of restat. + string err; + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + command_runner_.commands_ran_.clear(); + state_.Reset(); + + fs_.Tick(); + fs_.Create("in", ""); + fs_.Create("out2", ""); + + // Run a build, expect only the first command to run. + // It doesn't touch its output (due to being the "true" command), so + // we shouldn't run the dependent build. + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildWithLogTest, RestatSingleDependentOutputDirty) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "rule true\n" + " command = true\n" + " restat = 1\n" + "rule touch\n" + " command = touch\n" + "build out1: true in\n" + "build out2 out3: touch out1\n" + "build out4: touch out2\n" + )); + + // Create the necessary files + fs_.Create("in", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out4", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + + fs_.Tick(); + fs_.Create("in", ""); + fs_.RemoveFile("out3"); + + // Since "in" is missing, out1 will be built. Since "out3" is missing, + // out2 and out3 will be built even though "in" is not touched when built. + // Then, since out2 is rebuilt, out4 should be rebuilt -- the restat on the + // "true" rule should not lead to the "touch" edge writing out2 and out3 being + // cleard. + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out4", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); +} + +// Test scenario, in which an input file is removed, but output isn't changed +// https://github.com/ninja-build/ninja/issues/295 +TEST_F(BuildWithLogTest, RestatMissingInput) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "rule true\n" + " command = true\n" + " depfile = $out.d\n" + " restat = 1\n" + "rule cc\n" + " command = cc\n" + "build out1: true in\n" + "build out2: cc out1\n")); + + // Create all necessary files + fs_.Create("in", ""); + + // The implicit dependencies and the depfile itself + // are newer than the output + TimeStamp restat_mtime = fs_.Tick(); + fs_.Create("out1.d", "out1: will.be.deleted restat.file\n"); + fs_.Create("will.be.deleted", ""); + fs_.Create("restat.file", ""); + + // Run the build, out1 and out2 get built + string err; + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); + + // See that an entry in the logfile is created, capturing + // the right mtime + BuildLog::LogEntry* log_entry = build_log_.LookupByOutput("out1"); + ASSERT_TRUE(NULL != log_entry); + ASSERT_EQ(restat_mtime, log_entry->mtime); + + // Now remove a file, referenced from depfile, so that target becomes + // dirty, but the output does not change + fs_.RemoveFile("will.be.deleted"); + + // Trigger the build again - only out1 gets built + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + + // Check that the logfile entry remains correctly set + log_entry = build_log_.LookupByOutput("out1"); + ASSERT_TRUE(NULL != log_entry); + ASSERT_EQ(restat_mtime, log_entry->mtime); +} + +struct BuildDryRun : public BuildWithLogTest { + BuildDryRun() { + config_.dry_run = true; + } +}; + +TEST_F(BuildDryRun, AllCommandsShown) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule true\n" +" command = true\n" +" restat = 1\n" +"rule cc\n" +" command = cc\n" +" restat = 1\n" +"build out1: cc in\n" +"build out2: true out1\n" +"build out3: cat out2\n")); + + fs_.Create("out1", ""); + fs_.Create("out2", ""); + fs_.Create("out3", ""); + + fs_.Tick(); + + fs_.Create("in", ""); + + // "cc" touches out1, so we should build out2. But because "true" does not + // touch out2, we should cancel the build of out3. + string err; + EXPECT_TRUE(builder_.AddTarget("out3", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); +} + +// Test that RSP files are created when & where appropriate and deleted after +// successful execution. +TEST_F(BuildTest, RspFileSuccess) +{ + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "rule cat_rsp\n" + " command = cat $rspfile > $out\n" + " rspfile = $rspfile\n" + " rspfile_content = $long_command\n" + "rule cat_rsp_out\n" + " command = cat $rspfile > $out\n" + " rspfile = $out.rsp\n" + " rspfile_content = $long_command\n" + "build out1: cat in\n" + "build out2: cat_rsp in\n" + " rspfile = out 2.rsp\n" + " long_command = Some very long command\n" + "build out$ 3: cat_rsp_out in\n" + " long_command = Some very long command\n")); + + fs_.Create("out1", ""); + fs_.Create("out2", ""); + fs_.Create("out 3", ""); + + fs_.Tick(); + + fs_.Create("in", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AddTarget("out 3", &err)); + ASSERT_EQ("", err); + + size_t files_created = fs_.files_created_.size(); + size_t files_removed = fs_.files_removed_.size(); + + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + + // The RSP files were created + ASSERT_EQ(files_created + 2, fs_.files_created_.size()); + ASSERT_EQ(1u, fs_.files_created_.count("out 2.rsp")); + ASSERT_EQ(1u, fs_.files_created_.count("out 3.rsp")); + + // The RSP files were removed + ASSERT_EQ(files_removed + 2, fs_.files_removed_.size()); + ASSERT_EQ(1u, fs_.files_removed_.count("out 2.rsp")); + ASSERT_EQ(1u, fs_.files_removed_.count("out 3.rsp")); +} + +// Test that RSP file is created but not removed for commands, which fail +TEST_F(BuildTest, RspFileFailure) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "rule fail\n" + " command = fail\n" + " rspfile = $rspfile\n" + " rspfile_content = $long_command\n" + "build out: fail in\n" + " rspfile = out.rsp\n" + " long_command = Another very long command\n")); + + fs_.Create("out", ""); + fs_.Tick(); + fs_.Create("in", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out", &err)); + ASSERT_EQ("", err); + + size_t files_created = fs_.files_created_.size(); + size_t files_removed = fs_.files_removed_.size(); + + EXPECT_FALSE(builder_.Build(&err)); + ASSERT_EQ("subcommand failed", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + + // The RSP file was created + ASSERT_EQ(files_created + 1, fs_.files_created_.size()); + ASSERT_EQ(1u, fs_.files_created_.count("out.rsp")); + + // The RSP file was NOT removed + ASSERT_EQ(files_removed, fs_.files_removed_.size()); + ASSERT_EQ(0u, fs_.files_removed_.count("out.rsp")); + + // The RSP file contains what it should + ASSERT_EQ("Another very long command", fs_.files_["out.rsp"].contents); +} + +// Test that contents of the RSP file behaves like a regular part of +// command line, i.e. triggers a rebuild if changed +TEST_F(BuildWithLogTest, RspFileCmdLineChange) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "rule cat_rsp\n" + " command = cat $rspfile > $out\n" + " rspfile = $rspfile\n" + " rspfile_content = $long_command\n" + "build out: cat_rsp in\n" + " rspfile = out.rsp\n" + " long_command = Original very long command\n")); + + fs_.Create("out", ""); + fs_.Tick(); + fs_.Create("in", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out", &err)); + ASSERT_EQ("", err); + + // 1. Build for the 1st time (-> populate log) + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + + // 2. Build again (no change) + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out", &err)); + EXPECT_EQ("", err); + ASSERT_TRUE(builder_.AlreadyUpToDate()); + + // 3. Alter the entry in the logfile + // (to simulate a change in the command line between 2 builds) + BuildLog::LogEntry* log_entry = build_log_.LookupByOutput("out"); + ASSERT_TRUE(NULL != log_entry); + ASSERT_NO_FATAL_FAILURE(AssertHash( + "cat out.rsp > out;rspfile=Original very long command", + log_entry->command_hash)); + log_entry->command_hash++; // Change the command hash to something else. + // Now expect the target to be rebuilt + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out", &err)); + EXPECT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, InterruptCleanup) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule interrupt\n" +" command = interrupt\n" +"rule touch-interrupt\n" +" command = touch-interrupt\n" +"build out1: interrupt in1\n" +"build out2: touch-interrupt in2\n")); + + fs_.Create("out1", ""); + fs_.Create("out2", ""); + fs_.Tick(); + fs_.Create("in1", ""); + fs_.Create("in2", ""); + + // An untouched output of an interrupted command should be retained. + string err; + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + EXPECT_EQ("", err); + EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ("interrupted by user", err); + builder_.Cleanup(); + EXPECT_GT(fs_.Stat("out1", &err), 0); + err = ""; + + // A touched output of an interrupted command should be deleted. + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + EXPECT_EQ("", err); + EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ("interrupted by user", err); + builder_.Cleanup(); + EXPECT_EQ(0, fs_.Stat("out2", &err)); +} + +TEST_F(BuildTest, StatFailureAbortsBuild) { + const string kTooLongToStat(400, 'i'); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +("build " + kTooLongToStat + ": cat in\n").c_str())); + fs_.Create("in", ""); + + // This simulates a stat failure: + fs_.files_[kTooLongToStat].mtime = -1; + fs_.files_[kTooLongToStat].stat_error = "stat failed"; + + string err; + EXPECT_FALSE(builder_.AddTarget(kTooLongToStat, &err)); + EXPECT_EQ("stat failed", err); +} + +TEST_F(BuildTest, PhonyWithNoInputs) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build nonexistent: phony\n" +"build out1: cat || nonexistent\n" +"build out2: cat nonexistent\n")); + fs_.Create("out1", ""); + fs_.Create("out2", ""); + + // out1 should be up to date even though its input is dirty, because its + // order-only dependency has nothing to do. + string err; + EXPECT_TRUE(builder_.AddTarget("out1", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.AlreadyUpToDate()); + + // out2 should still be out of date though, because its input is dirty. + err.clear(); + command_runner_.commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, DepsGccWithEmptyDepfileErrorsOut) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n" +" command = cc\n" +" deps = gcc\n" +"build out: cc\n")); + Dirty("out"); + + string err; + EXPECT_TRUE(builder_.AddTarget("out", &err)); + ASSERT_EQ("", err); + EXPECT_FALSE(builder_.AlreadyUpToDate()); + + EXPECT_FALSE(builder_.Build(&err)); + ASSERT_EQ("subcommand failed", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, StatusFormatElapsed) { + status_.BuildStarted(); + // Before any task is done, the elapsed time must be zero. + EXPECT_EQ("[%/e0.000]", + status_.FormatProgressStatus("[%%/e%e]", + BuildStatus::kEdgeStarted)); +} + +TEST_F(BuildTest, StatusFormatReplacePlaceholder) { + EXPECT_EQ("[%/s0/t0/r0/u0/f0]", + status_.FormatProgressStatus("[%%/s%s/t%t/r%r/u%u/f%f]", + BuildStatus::kEdgeStarted)); +} + +TEST_F(BuildTest, FailedDepsParse) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build bad_deps.o: cat in1\n" +" deps = gcc\n" +" depfile = in1.d\n")); + + string err; + EXPECT_TRUE(builder_.AddTarget("bad_deps.o", &err)); + ASSERT_EQ("", err); + + // These deps will fail to parse, as they should only have one + // path to the left of the colon. + fs_.Create("in1.d", "AAA BBB"); + + EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ("subcommand failed", err); +} + +/// Tests of builds involving deps logs necessarily must span +/// multiple builds. We reuse methods on BuildTest but not the +/// builder_ it sets up, because we want pristine objects for +/// each build. +struct BuildWithDepsLogTest : public BuildTest { + BuildWithDepsLogTest() {} + + virtual void SetUp() { + BuildTest::SetUp(); + + temp_dir_.CreateAndEnter("BuildWithDepsLogTest"); + } + + virtual void TearDown() { + temp_dir_.Cleanup(); + } + + ScopedTempDir temp_dir_; + + /// Shadow parent class builder_ so we don't accidentally use it. + void* builder_; +}; + +/// Run a straightforwad build where the deps log is used. +TEST_F(BuildWithDepsLogTest, Straightforward) { + string err; + // Note: in1 was created by the superclass SetUp(). + const char* manifest = + "build out: cat in1\n" + " deps = gcc\n" + " depfile = in1.d\n"; + { + State state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // Run the build once, everything should be ok. + DepsLog deps_log; + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + ASSERT_EQ("", err); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + EXPECT_TRUE(builder.AddTarget("out", &err)); + ASSERT_EQ("", err); + fs_.Create("in1.d", "out: in2"); + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + // The deps file should have been removed. + EXPECT_EQ(0, fs_.Stat("in1.d", &err)); + // Recreate it for the next step. + fs_.Create("in1.d", "out: in2"); + deps_log.Close(); + builder.command_runner_.release(); + } + + { + State state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // Touch the file only mentioned in the deps. + fs_.Tick(); + fs_.Create("in2", ""); + + // Run the build again. + DepsLog deps_log; + ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err)); + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + command_runner_.commands_ran_.clear(); + EXPECT_TRUE(builder.AddTarget("out", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + // We should have rebuilt the output due to in2 being + // out of date. + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); + + builder.command_runner_.release(); + } +} + +/// Verify that obsolete dependency info causes a rebuild. +/// 1) Run a successful build where everything has time t, record deps. +/// 2) Move input/output to time t+1 -- despite files in alignment, +/// should still need to rebuild due to deps at older time. +TEST_F(BuildWithDepsLogTest, ObsoleteDeps) { + string err; + // Note: in1 was created by the superclass SetUp(). + const char* manifest = + "build out: cat in1\n" + " deps = gcc\n" + " depfile = in1.d\n"; + { + // Run an ordinary build that gathers dependencies. + fs_.Create("in1", ""); + fs_.Create("in1.d", "out: "); + + State state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // Run the build once, everything should be ok. + DepsLog deps_log; + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + ASSERT_EQ("", err); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + EXPECT_TRUE(builder.AddTarget("out", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + deps_log.Close(); + builder.command_runner_.release(); + } + + // Push all files one tick forward so that only the deps are out + // of date. + fs_.Tick(); + fs_.Create("in1", ""); + fs_.Create("out", ""); + + // The deps file should have been removed, so no need to timestamp it. + EXPECT_EQ(0, fs_.Stat("in1.d", &err)); + + { + State state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + DepsLog deps_log; + ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err)); + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + command_runner_.commands_ran_.clear(); + EXPECT_TRUE(builder.AddTarget("out", &err)); + ASSERT_EQ("", err); + + // Recreate the deps file here because the build expects them to exist. + fs_.Create("in1.d", "out: "); + + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + // We should have rebuilt the output due to the deps being + // out of date. + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); + + builder.command_runner_.release(); + } +} + +TEST_F(BuildWithDepsLogTest, DepsIgnoredInDryRun) { + const char* manifest = + "build out: cat in1\n" + " deps = gcc\n" + " depfile = in1.d\n"; + + fs_.Create("out", ""); + fs_.Tick(); + fs_.Create("in1", ""); + + State state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // The deps log is NULL in dry runs. + config_.dry_run = true; + Builder builder(&state, config_, NULL, NULL, &fs_); + builder.command_runner_.reset(&command_runner_); + command_runner_.commands_ran_.clear(); + + string err; + EXPECT_TRUE(builder.AddTarget("out", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder.Build(&err)); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + + builder.command_runner_.release(); +} + +/// Check that a restat rule generating a header cancels compilations correctly. +TEST_F(BuildTest, RestatDepfileDependency) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule true\n" +" command = true\n" // Would be "write if out-of-date" in reality. +" restat = 1\n" +"build header.h: true header.in\n" +"build out: cat in1\n" +" depfile = in1.d\n")); + + fs_.Create("header.h", ""); + fs_.Create("in1.d", "out: header.h"); + fs_.Tick(); + fs_.Create("header.in", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); +} + +/// Check that a restat rule generating a header cancels compilations correctly, +/// depslog case. +TEST_F(BuildWithDepsLogTest, RestatDepfileDependencyDepsLog) { + string err; + // Note: in1 was created by the superclass SetUp(). + const char* manifest = + "rule true\n" + " command = true\n" // Would be "write if out-of-date" in reality. + " restat = 1\n" + "build header.h: true header.in\n" + "build out: cat in1\n" + " deps = gcc\n" + " depfile = in1.d\n"; + { + State state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // Run the build once, everything should be ok. + DepsLog deps_log; + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + ASSERT_EQ("", err); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + EXPECT_TRUE(builder.AddTarget("out", &err)); + ASSERT_EQ("", err); + fs_.Create("in1.d", "out: header.h"); + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + deps_log.Close(); + builder.command_runner_.release(); + } + + { + State state; + ASSERT_NO_FATAL_FAILURE(AddCatRule(&state)); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // Touch the input of the restat rule. + fs_.Tick(); + fs_.Create("header.in", ""); + + // Run the build again. + DepsLog deps_log; + ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err)); + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + command_runner_.commands_ran_.clear(); + EXPECT_TRUE(builder.AddTarget("out", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + // Rule "true" should have run again, but the build of "out" should have + // been cancelled due to restat propagating through the depfile header. + EXPECT_EQ(1u, command_runner_.commands_ran_.size()); + + builder.command_runner_.release(); + } +} + +TEST_F(BuildWithDepsLogTest, DepFileOKDepsLog) { + string err; + const char* manifest = + "rule cc\n command = cc $in\n depfile = $out.d\n deps = gcc\n" + "build fo$ o.o: cc foo.c\n"; + + fs_.Create("foo.c", ""); + + { + State state; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // Run the build once, everything should be ok. + DepsLog deps_log; + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + ASSERT_EQ("", err); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + EXPECT_TRUE(builder.AddTarget("fo o.o", &err)); + ASSERT_EQ("", err); + fs_.Create("fo o.o.d", "fo\\ o.o: blah.h bar.h\n"); + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + deps_log.Close(); + builder.command_runner_.release(); + } + + { + State state; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + DepsLog deps_log; + ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err)); + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + ASSERT_EQ("", err); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + + Edge* edge = state.edges_.back(); + + state.GetNode("bar.h", 0)->MarkDirty(); // Mark bar.h as missing. + EXPECT_TRUE(builder.AddTarget("fo o.o", &err)); + ASSERT_EQ("", err); + + // Expect three new edges: one generating fo o.o, and two more from + // loading the depfile. + ASSERT_EQ(3u, state.edges_.size()); + // Expect our edge to now have three inputs: foo.c and two headers. + ASSERT_EQ(3u, edge->inputs_.size()); + + // Expect the command line we generate to only use the original input. + ASSERT_EQ("cc foo.c", edge->EvaluateCommand()); + + deps_log.Close(); + builder.command_runner_.release(); + } +} + +#ifdef _WIN32 +TEST_F(BuildWithDepsLogTest, DepFileDepsLogCanonicalize) { + string err; + const char* manifest = + "rule cc\n command = cc $in\n depfile = $out.d\n deps = gcc\n" + "build a/b\\c\\d/e/fo$ o.o: cc x\\y/z\\foo.c\n"; + + fs_.Create("x/y/z/foo.c", ""); + + { + State state; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + // Run the build once, everything should be ok. + DepsLog deps_log; + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + ASSERT_EQ("", err); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + EXPECT_TRUE(builder.AddTarget("a/b/c/d/e/fo o.o", &err)); + ASSERT_EQ("", err); + // Note, different slashes from manifest. + fs_.Create("a/b\\c\\d/e/fo o.o.d", + "a\\b\\c\\d\\e\\fo\\ o.o: blah.h bar.h\n"); + EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ("", err); + + deps_log.Close(); + builder.command_runner_.release(); + } + + { + State state; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest)); + + DepsLog deps_log; + ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err)); + ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err)); + ASSERT_EQ("", err); + + Builder builder(&state, config_, NULL, &deps_log, &fs_); + builder.command_runner_.reset(&command_runner_); + + Edge* edge = state.edges_.back(); + + state.GetNode("bar.h", 0)->MarkDirty(); // Mark bar.h as missing. + EXPECT_TRUE(builder.AddTarget("a/b/c/d/e/fo o.o", &err)); + ASSERT_EQ("", err); + + // Expect three new edges: one generating fo o.o, and two more from + // loading the depfile. + ASSERT_EQ(3u, state.edges_.size()); + // Expect our edge to now have three inputs: foo.c and two headers. + ASSERT_EQ(3u, edge->inputs_.size()); + + // Expect the command line we generate to only use the original input. + // Note, slashes from manifest, not .d. + ASSERT_EQ("cc x\\y/z\\foo.c", edge->EvaluateCommand()); + + deps_log.Close(); + builder.command_runner_.release(); + } +} +#endif + +/// Check that a restat rule doesn't clear an edge if the depfile is missing. +/// Follows from: https://github.com/ninja-build/ninja/issues/603 +TEST_F(BuildTest, RestatMissingDepfile) { +const char* manifest = +"rule true\n" +" command = true\n" // Would be "write if out-of-date" in reality. +" restat = 1\n" +"build header.h: true header.in\n" +"build out: cat header.h\n" +" depfile = out.d\n"; + + fs_.Create("header.h", ""); + fs_.Tick(); + fs_.Create("out", ""); + fs_.Create("header.in", ""); + + // Normally, only 'header.h' would be rebuilt, as + // its rule doesn't touch the output and has 'restat=1' set. + // But we are also missing the depfile for 'out', + // which should force its command to run anyway! + RebuildTarget("out", manifest); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); +} + +/// Check that a restat rule doesn't clear an edge if the deps are missing. +/// https://github.com/ninja-build/ninja/issues/603 +TEST_F(BuildWithDepsLogTest, RestatMissingDepfileDepslog) { + string err; + const char* manifest = +"rule true\n" +" command = true\n" // Would be "write if out-of-date" in reality. +" restat = 1\n" +"build header.h: true header.in\n" +"build out: cat header.h\n" +" deps = gcc\n" +" depfile = out.d\n"; + + // Build once to populate ninja deps logs from out.d + fs_.Create("header.in", ""); + fs_.Create("out.d", "out: header.h"); + fs_.Create("header.h", ""); + + RebuildTarget("out", manifest, "build_log", "ninja_deps"); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); + + // Sanity: this rebuild should be NOOP + RebuildTarget("out", manifest, "build_log", "ninja_deps"); + ASSERT_EQ(0u, command_runner_.commands_ran_.size()); + + // Touch 'header.in', blank dependencies log (create a different one). + // Building header.h triggers 'restat' outputs cleanup. + // Validate that out is rebuilt netherless, as deps are missing. + fs_.Tick(); + fs_.Create("header.in", ""); + + // (switch to a new blank deps_log "ninja_deps2") + RebuildTarget("out", manifest, "build_log", "ninja_deps2"); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); + + // Sanity: this build should be NOOP + RebuildTarget("out", manifest, "build_log", "ninja_deps2"); + ASSERT_EQ(0u, command_runner_.commands_ran_.size()); + + // Check that invalidating deps by target timestamp also works here + // Repeat the test but touch target instead of blanking the log. + fs_.Tick(); + fs_.Create("header.in", ""); + fs_.Create("out", ""); + RebuildTarget("out", manifest, "build_log", "ninja_deps2"); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); + + // And this build should be NOOP again + RebuildTarget("out", manifest, "build_log", "ninja_deps2"); + ASSERT_EQ(0u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, WrongOutputInDepfileCausesRebuild) { + string err; + const char* manifest = +"rule cc\n" +" command = cc $in\n" +" depfile = $out.d\n" +"build foo.o: cc foo.c\n"; + + fs_.Create("foo.c", ""); + fs_.Create("foo.o", ""); + fs_.Create("header.h", ""); + fs_.Create("foo.o.d", "bar.o.d: header.h\n"); + + RebuildTarget("foo.o", manifest, "build_log", "ninja_deps"); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); +} + +TEST_F(BuildTest, Console) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule console\n" +" command = console\n" +" pool = console\n" +"build cons: console in.txt\n")); + + fs_.Create("in.txt", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("cons", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); +} diff --git a/src/3rdparty/ninja/src/canon_perftest.cc b/src/3rdparty/ninja/src/canon_perftest.cc new file mode 100644 index 00000000000..03f4a2f1f95 --- /dev/null +++ b/src/3rdparty/ninja/src/canon_perftest.cc @@ -0,0 +1,57 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "util.h" +#include "metrics.h" + +const char kPath[] = + "../../third_party/WebKit/Source/WebCore/" + "platform/leveldb/LevelDBWriteBatch.cpp"; + +int main() { + vector times; + string err; + + char buf[200]; + size_t len = strlen(kPath); + strcpy(buf, kPath); + + for (int j = 0; j < 5; ++j) { + const int kNumRepetitions = 2000000; + int64_t start = GetTimeMillis(); + uint64_t slash_bits; + for (int i = 0; i < kNumRepetitions; ++i) { + CanonicalizePath(buf, &len, &slash_bits, &err); + } + int delta = (int)(GetTimeMillis() - start); + times.push_back(delta); + } + + int min = times[0]; + int max = times[0]; + float total = 0; + for (size_t i = 0; i < times.size(); ++i) { + total += times[i]; + if (times[i] < min) + min = times[i]; + else if (times[i] > max) + max = times[i]; + } + + printf("min %dms max %dms avg %.1fms\n", + min, max, total / times.size()); +} diff --git a/src/3rdparty/ninja/src/clean.cc b/src/3rdparty/ninja/src/clean.cc new file mode 100644 index 00000000000..1d6ba9e9679 --- /dev/null +++ b/src/3rdparty/ninja/src/clean.cc @@ -0,0 +1,263 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "clean.h" + +#include +#include + +#include "disk_interface.h" +#include "graph.h" +#include "state.h" +#include "util.h" + +Cleaner::Cleaner(State* state, const BuildConfig& config) + : state_(state), + config_(config), + removed_(), + cleaned_(), + cleaned_files_count_(0), + disk_interface_(new RealDiskInterface), + status_(0) { +} + +Cleaner::Cleaner(State* state, + const BuildConfig& config, + DiskInterface* disk_interface) + : state_(state), + config_(config), + removed_(), + cleaned_(), + cleaned_files_count_(0), + disk_interface_(disk_interface), + status_(0) { +} + +int Cleaner::RemoveFile(const string& path) { + return disk_interface_->RemoveFile(path); +} + +bool Cleaner::FileExists(const string& path) { + string err; + TimeStamp mtime = disk_interface_->Stat(path, &err); + if (mtime == -1) + Error("%s", err.c_str()); + return mtime > 0; // Treat Stat() errors as "file does not exist". +} + +void Cleaner::Report(const string& path) { + ++cleaned_files_count_; + if (IsVerbose()) + printf("Remove %s\n", path.c_str()); +} + +void Cleaner::Remove(const string& path) { + if (!IsAlreadyRemoved(path)) { + removed_.insert(path); + if (config_.dry_run) { + if (FileExists(path)) + Report(path); + } else { + int ret = RemoveFile(path); + if (ret == 0) + Report(path); + else if (ret == -1) + status_ = 1; + } + } +} + +bool Cleaner::IsAlreadyRemoved(const string& path) { + set::iterator i = removed_.find(path); + return (i != removed_.end()); +} + +void Cleaner::RemoveEdgeFiles(Edge* edge) { + string depfile = edge->GetUnescapedDepfile(); + if (!depfile.empty()) + Remove(depfile); + + string rspfile = edge->GetUnescapedRspfile(); + if (!rspfile.empty()) + Remove(rspfile); +} + +void Cleaner::PrintHeader() { + if (config_.verbosity == BuildConfig::QUIET) + return; + printf("Cleaning..."); + if (IsVerbose()) + printf("\n"); + else + printf(" "); +} + +void Cleaner::PrintFooter() { + if (config_.verbosity == BuildConfig::QUIET) + return; + printf("%d files.\n", cleaned_files_count_); +} + +int Cleaner::CleanAll(bool generator) { + Reset(); + PrintHeader(); + for (vector::iterator e = state_->edges_.begin(); + e != state_->edges_.end(); ++e) { + // Do not try to remove phony targets + if ((*e)->is_phony()) + continue; + // Do not remove generator's files unless generator specified. + if (!generator && (*e)->GetBindingBool("generator")) + continue; + for (vector::iterator out_node = (*e)->outputs_.begin(); + out_node != (*e)->outputs_.end(); ++out_node) { + Remove((*out_node)->path()); + } + + RemoveEdgeFiles(*e); + } + PrintFooter(); + return status_; +} + +void Cleaner::DoCleanTarget(Node* target) { + if (Edge* e = target->in_edge()) { + // Do not try to remove phony targets + if (!e->is_phony()) { + Remove(target->path()); + RemoveEdgeFiles(e); + } + for (vector::iterator n = e->inputs_.begin(); n != e->inputs_.end(); + ++n) { + Node* next = *n; + // call DoCleanTarget recursively if this node has not been visited + if (cleaned_.count(next) == 0) { + DoCleanTarget(next); + } + } + } + + // mark this target to be cleaned already + cleaned_.insert(target); +} + +int Cleaner::CleanTarget(Node* target) { + assert(target); + + Reset(); + PrintHeader(); + DoCleanTarget(target); + PrintFooter(); + return status_; +} + +int Cleaner::CleanTarget(const char* target) { + assert(target); + + Reset(); + Node* node = state_->LookupNode(target); + if (node) { + CleanTarget(node); + } else { + Error("unknown target '%s'", target); + status_ = 1; + } + return status_; +} + +int Cleaner::CleanTargets(int target_count, char* targets[]) { + Reset(); + PrintHeader(); + for (int i = 0; i < target_count; ++i) { + const char* target_name = targets[i]; + Node* target = state_->LookupNode(target_name); + if (target) { + if (IsVerbose()) + printf("Target %s\n", target_name); + DoCleanTarget(target); + } else { + Error("unknown target '%s'", target_name); + status_ = 1; + } + } + PrintFooter(); + return status_; +} + +void Cleaner::DoCleanRule(const Rule* rule) { + assert(rule); + + for (vector::iterator e = state_->edges_.begin(); + e != state_->edges_.end(); ++e) { + if ((*e)->rule().name() == rule->name()) { + for (vector::iterator out_node = (*e)->outputs_.begin(); + out_node != (*e)->outputs_.end(); ++out_node) { + Remove((*out_node)->path()); + RemoveEdgeFiles(*e); + } + } + } +} + +int Cleaner::CleanRule(const Rule* rule) { + assert(rule); + + Reset(); + PrintHeader(); + DoCleanRule(rule); + PrintFooter(); + return status_; +} + +int Cleaner::CleanRule(const char* rule) { + assert(rule); + + Reset(); + const Rule* r = state_->bindings_.LookupRule(rule); + if (r) { + CleanRule(r); + } else { + Error("unknown rule '%s'", rule); + status_ = 1; + } + return status_; +} + +int Cleaner::CleanRules(int rule_count, char* rules[]) { + assert(rules); + + Reset(); + PrintHeader(); + for (int i = 0; i < rule_count; ++i) { + const char* rule_name = rules[i]; + const Rule* rule = state_->bindings_.LookupRule(rule_name); + if (rule) { + if (IsVerbose()) + printf("Rule %s\n", rule_name); + DoCleanRule(rule); + } else { + Error("unknown rule '%s'", rule_name); + status_ = 1; + } + } + PrintFooter(); + return status_; +} + +void Cleaner::Reset() { + status_ = 0; + cleaned_files_count_ = 0; + removed_.clear(); + cleaned_.clear(); +} diff --git a/src/3rdparty/ninja/src/clean.h b/src/3rdparty/ninja/src/clean.h new file mode 100644 index 00000000000..19432ab2d44 --- /dev/null +++ b/src/3rdparty/ninja/src/clean.h @@ -0,0 +1,107 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_CLEAN_H_ +#define NINJA_CLEAN_H_ + +#include +#include + +#include "build.h" + +using namespace std; + +struct State; +struct Node; +struct Rule; +struct DiskInterface; + +struct Cleaner { + /// Build a cleaner object with a real disk interface. + Cleaner(State* state, const BuildConfig& config); + + /// Build a cleaner object with the given @a disk_interface + /// (Useful for testing). + Cleaner(State* state, + const BuildConfig& config, + DiskInterface* disk_interface); + + /// Clean the given @a target and all the file built for it. + /// @return non-zero if an error occurs. + int CleanTarget(Node* target); + /// Clean the given target @a target. + /// @return non-zero if an error occurs. + int CleanTarget(const char* target); + /// Clean the given target @a targets. + /// @return non-zero if an error occurs. + int CleanTargets(int target_count, char* targets[]); + + /// Clean all built files, except for files created by generator rules. + /// @param generator If set, also clean files created by generator rules. + /// @return non-zero if an error occurs. + int CleanAll(bool generator = false); + + /// Clean all the file built with the given rule @a rule. + /// @return non-zero if an error occurs. + int CleanRule(const Rule* rule); + /// Clean the file produced by the given @a rule. + /// @return non-zero if an error occurs. + int CleanRule(const char* rule); + /// Clean the file produced by the given @a rules. + /// @return non-zero if an error occurs. + int CleanRules(int rule_count, char* rules[]); + + /// @return the number of file cleaned. + int cleaned_files_count() const { + return cleaned_files_count_; + } + + /// @return whether the cleaner is in verbose mode. + bool IsVerbose() const { + return (config_.verbosity != BuildConfig::QUIET + && (config_.verbosity == BuildConfig::VERBOSE || config_.dry_run)); + } + + private: + /// Remove the file @a path. + /// @return whether the file has been removed. + int RemoveFile(const string& path); + /// @returns whether the file @a path exists. + bool FileExists(const string& path); + void Report(const string& path); + + /// Remove the given @a path file only if it has not been already removed. + void Remove(const string& path); + /// @return whether the given @a path has already been removed. + bool IsAlreadyRemoved(const string& path); + /// Remove the depfile and rspfile for an Edge. + void RemoveEdgeFiles(Edge* edge); + + /// Helper recursive method for CleanTarget(). + void DoCleanTarget(Node* target); + void PrintHeader(); + void PrintFooter(); + void DoCleanRule(const Rule* rule); + void Reset(); + + State* state_; + const BuildConfig& config_; + set removed_; + set cleaned_; + int cleaned_files_count_; + DiskInterface* disk_interface_; + int status_; +}; + +#endif // NINJA_CLEAN_H_ diff --git a/src/3rdparty/ninja/src/clean_test.cc b/src/3rdparty/ninja/src/clean_test.cc new file mode 100644 index 00000000000..395343b314f --- /dev/null +++ b/src/3rdparty/ninja/src/clean_test.cc @@ -0,0 +1,407 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "clean.h" +#include "build.h" + +#include "test.h" + +struct CleanTest : public StateTestWithBuiltinRules { + VirtualFileSystem fs_; + BuildConfig config_; + virtual void SetUp() { + config_.verbosity = BuildConfig::QUIET; + } +}; + +TEST_F(CleanTest, CleanAll) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build in1: cat src1\n" +"build out1: cat in1\n" +"build in2: cat src2\n" +"build out2: cat in2\n")); + fs_.Create("in1", ""); + fs_.Create("out1", ""); + fs_.Create("in2", ""); + fs_.Create("out2", ""); + + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(4, cleaner.cleaned_files_count()); + EXPECT_EQ(4u, fs_.files_removed_.size()); + + // Check they are removed. + string err; + EXPECT_EQ(0, fs_.Stat("in1", &err)); + EXPECT_EQ(0, fs_.Stat("out1", &err)); + EXPECT_EQ(0, fs_.Stat("in2", &err)); + EXPECT_EQ(0, fs_.Stat("out2", &err)); + fs_.files_removed_.clear(); + + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(0, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanAllDryRun) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build in1: cat src1\n" +"build out1: cat in1\n" +"build in2: cat src2\n" +"build out2: cat in2\n")); + fs_.Create("in1", ""); + fs_.Create("out1", ""); + fs_.Create("in2", ""); + fs_.Create("out2", ""); + + config_.dry_run = true; + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(4, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); + + // Check they are not removed. + string err; + EXPECT_LT(0, fs_.Stat("in1", &err)); + EXPECT_LT(0, fs_.Stat("out1", &err)); + EXPECT_LT(0, fs_.Stat("in2", &err)); + EXPECT_LT(0, fs_.Stat("out2", &err)); + fs_.files_removed_.clear(); + + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(4, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanTarget) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build in1: cat src1\n" +"build out1: cat in1\n" +"build in2: cat src2\n" +"build out2: cat in2\n")); + fs_.Create("in1", ""); + fs_.Create("out1", ""); + fs_.Create("in2", ""); + fs_.Create("out2", ""); + + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + ASSERT_EQ(0, cleaner.CleanTarget("out1")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); + + // Check they are removed. + string err; + EXPECT_EQ(0, fs_.Stat("in1", &err)); + EXPECT_EQ(0, fs_.Stat("out1", &err)); + EXPECT_LT(0, fs_.Stat("in2", &err)); + EXPECT_LT(0, fs_.Stat("out2", &err)); + fs_.files_removed_.clear(); + + ASSERT_EQ(0, cleaner.CleanTarget("out1")); + EXPECT_EQ(0, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanTargetDryRun) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build in1: cat src1\n" +"build out1: cat in1\n" +"build in2: cat src2\n" +"build out2: cat in2\n")); + fs_.Create("in1", ""); + fs_.Create("out1", ""); + fs_.Create("in2", ""); + fs_.Create("out2", ""); + + config_.dry_run = true; + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + ASSERT_EQ(0, cleaner.CleanTarget("out1")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); + + // Check they are not removed. + string err; + EXPECT_LT(0, fs_.Stat("in1", &err)); + EXPECT_LT(0, fs_.Stat("out1", &err)); + EXPECT_LT(0, fs_.Stat("in2", &err)); + EXPECT_LT(0, fs_.Stat("out2", &err)); + fs_.files_removed_.clear(); + + ASSERT_EQ(0, cleaner.CleanTarget("out1")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanRule) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cat_e\n" +" command = cat -e $in > $out\n" +"build in1: cat_e src1\n" +"build out1: cat in1\n" +"build in2: cat_e src2\n" +"build out2: cat in2\n")); + fs_.Create("in1", ""); + fs_.Create("out1", ""); + fs_.Create("in2", ""); + fs_.Create("out2", ""); + + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + ASSERT_EQ(0, cleaner.CleanRule("cat_e")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); + + // Check they are removed. + string err; + EXPECT_EQ(0, fs_.Stat("in1", &err)); + EXPECT_LT(0, fs_.Stat("out1", &err)); + EXPECT_EQ(0, fs_.Stat("in2", &err)); + EXPECT_LT(0, fs_.Stat("out2", &err)); + fs_.files_removed_.clear(); + + ASSERT_EQ(0, cleaner.CleanRule("cat_e")); + EXPECT_EQ(0, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanRuleDryRun) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cat_e\n" +" command = cat -e $in > $out\n" +"build in1: cat_e src1\n" +"build out1: cat in1\n" +"build in2: cat_e src2\n" +"build out2: cat in2\n")); + fs_.Create("in1", ""); + fs_.Create("out1", ""); + fs_.Create("in2", ""); + fs_.Create("out2", ""); + + config_.dry_run = true; + Cleaner cleaner(&state_, config_, &fs_); + + ASSERT_EQ(0, cleaner.cleaned_files_count()); + ASSERT_EQ(0, cleaner.CleanRule("cat_e")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); + + // Check they are not removed. + string err; + EXPECT_LT(0, fs_.Stat("in1", &err)); + EXPECT_LT(0, fs_.Stat("out1", &err)); + EXPECT_LT(0, fs_.Stat("in2", &err)); + EXPECT_LT(0, fs_.Stat("out2", &err)); + fs_.files_removed_.clear(); + + ASSERT_EQ(0, cleaner.CleanRule("cat_e")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(0u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanRuleGenerator) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule regen\n" +" command = cat $in > $out\n" +" generator = 1\n" +"build out1: cat in1\n" +"build out2: regen in2\n")); + fs_.Create("out1", ""); + fs_.Create("out2", ""); + + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(1, cleaner.cleaned_files_count()); + EXPECT_EQ(1u, fs_.files_removed_.size()); + + fs_.Create("out1", ""); + + EXPECT_EQ(0, cleaner.CleanAll(/*generator=*/true)); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanDepFile) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n" +" command = cc $in > $out\n" +" depfile = $out.d\n" +"build out1: cc in1\n")); + fs_.Create("out1", ""); + fs_.Create("out1.d", ""); + + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanDepFileOnCleanTarget) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n" +" command = cc $in > $out\n" +" depfile = $out.d\n" +"build out1: cc in1\n")); + fs_.Create("out1", ""); + fs_.Create("out1.d", ""); + + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_EQ(0, cleaner.CleanTarget("out1")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanDepFileOnCleanRule) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n" +" command = cc $in > $out\n" +" depfile = $out.d\n" +"build out1: cc in1\n")); + fs_.Create("out1", ""); + fs_.Create("out1.d", ""); + + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_EQ(0, cleaner.CleanRule("cc")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanRspFile) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc\n" +" command = cc $in > $out\n" +" rspfile = $rspfile\n" +" rspfile_content=$in\n" +"build out1: cc in1\n" +" rspfile = cc1.rsp\n")); + fs_.Create("out1", ""); + fs_.Create("cc1.rsp", ""); + + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_EQ(2u, fs_.files_removed_.size()); +} + +TEST_F(CleanTest, CleanRsp) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cat_rsp \n" +" command = cat $rspfile > $out\n" +" rspfile = $rspfile\n" +" rspfile_content = $in\n" +"build in1: cat src1\n" +"build out1: cat in1\n" +"build in2: cat_rsp src2\n" +" rspfile=in2.rsp\n" +"build out2: cat_rsp in2\n" +" rspfile=out2.rsp\n" +)); + fs_.Create("in1", ""); + fs_.Create("out1", ""); + fs_.Create("in2.rsp", ""); + fs_.Create("out2.rsp", ""); + fs_.Create("in2", ""); + fs_.Create("out2", ""); + + Cleaner cleaner(&state_, config_, &fs_); + ASSERT_EQ(0, cleaner.cleaned_files_count()); + ASSERT_EQ(0, cleaner.CleanTarget("out1")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + ASSERT_EQ(0, cleaner.CleanTarget("in2")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + ASSERT_EQ(0, cleaner.CleanRule("cat_rsp")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + + EXPECT_EQ(6u, fs_.files_removed_.size()); + + // Check they are removed. + string err; + EXPECT_EQ(0, fs_.Stat("in1", &err)); + EXPECT_EQ(0, fs_.Stat("out1", &err)); + EXPECT_EQ(0, fs_.Stat("in2", &err)); + EXPECT_EQ(0, fs_.Stat("out2", &err)); + EXPECT_EQ(0, fs_.Stat("in2.rsp", &err)); + EXPECT_EQ(0, fs_.Stat("out2.rsp", &err)); +} + +TEST_F(CleanTest, CleanFailure) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "build dir: cat src1\n")); + fs_.MakeDir("dir"); + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_NE(0, cleaner.CleanAll()); +} + +TEST_F(CleanTest, CleanPhony) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build phony: phony t1 t2\n" +"build t1: cat\n" +"build t2: cat\n")); + + fs_.Create("phony", ""); + fs_.Create("t1", ""); + fs_.Create("t2", ""); + + // Check that CleanAll does not remove "phony". + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_LT(0, fs_.Stat("phony", &err)); + + fs_.Create("t1", ""); + fs_.Create("t2", ""); + + // Check that CleanTarget does not remove "phony". + EXPECT_EQ(0, cleaner.CleanTarget("phony")); + EXPECT_EQ(2, cleaner.cleaned_files_count()); + EXPECT_LT(0, fs_.Stat("phony", &err)); +} + +TEST_F(CleanTest, CleanDepFileAndRspFileWithSpaces) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule cc_dep\n" +" command = cc $in > $out\n" +" depfile = $out.d\n" +"rule cc_rsp\n" +" command = cc $in > $out\n" +" rspfile = $out.rsp\n" +" rspfile_content = $in\n" +"build out$ 1: cc_dep in$ 1\n" +"build out$ 2: cc_rsp in$ 1\n" +)); + fs_.Create("out 1", ""); + fs_.Create("out 2", ""); + fs_.Create("out 1.d", ""); + fs_.Create("out 2.rsp", ""); + + Cleaner cleaner(&state_, config_, &fs_); + EXPECT_EQ(0, cleaner.CleanAll()); + EXPECT_EQ(4, cleaner.cleaned_files_count()); + EXPECT_EQ(4u, fs_.files_removed_.size()); + + string err; + EXPECT_EQ(0, fs_.Stat("out 1", &err)); + EXPECT_EQ(0, fs_.Stat("out 2", &err)); + EXPECT_EQ(0, fs_.Stat("out 1.d", &err)); + EXPECT_EQ(0, fs_.Stat("out 2.rsp", &err)); +} diff --git a/src/3rdparty/ninja/src/clparser.cc b/src/3rdparty/ninja/src/clparser.cc new file mode 100644 index 00000000000..7994c06f4ee --- /dev/null +++ b/src/3rdparty/ninja/src/clparser.cc @@ -0,0 +1,126 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "clparser.h" + +#include +#include +#include + +#include "metrics.h" +#include "string_piece_util.h" + +#ifdef _WIN32 +#include "includes_normalize.h" +#include "string_piece.h" +#else +#include "util.h" +#endif + +namespace { + +/// Return true if \a input ends with \a needle. +bool EndsWith(const string& input, const string& needle) { + return (input.size() >= needle.size() && + input.substr(input.size() - needle.size()) == needle); +} + +} // anonymous namespace + +// static +string CLParser::FilterShowIncludes(const string& line, + const string& deps_prefix) { + const string kDepsPrefixEnglish = "Note: including file: "; + const char* in = line.c_str(); + const char* end = in + line.size(); + const string& prefix = deps_prefix.empty() ? kDepsPrefixEnglish : deps_prefix; + if (end - in > (int)prefix.size() && + memcmp(in, prefix.c_str(), (int)prefix.size()) == 0) { + in += prefix.size(); + while (*in == ' ') + ++in; + return line.substr(in - line.c_str()); + } + return ""; +} + +// static +bool CLParser::IsSystemInclude(string path) { + transform(path.begin(), path.end(), path.begin(), ToLowerASCII); + // TODO: this is a heuristic, perhaps there's a better way? + return (path.find("program files") != string::npos || + path.find("microsoft visual studio") != string::npos); +} + +// static +bool CLParser::FilterInputFilename(string line) { + transform(line.begin(), line.end(), line.begin(), ToLowerASCII); + // TODO: other extensions, like .asm? + return EndsWith(line, ".c") || + EndsWith(line, ".cc") || + EndsWith(line, ".cxx") || + EndsWith(line, ".cpp"); +} + +// static +bool CLParser::Parse(const string& output, const string& deps_prefix, + string* filtered_output, string* err) { + METRIC_RECORD("CLParser::Parse"); + + // Loop over all lines in the output to process them. + assert(&output != filtered_output); + size_t start = 0; +#ifdef _WIN32 + IncludesNormalize normalizer("."); +#endif + + while (start < output.size()) { + size_t end = output.find_first_of("\r\n", start); + if (end == string::npos) + end = output.size(); + string line = output.substr(start, end - start); + + string include = FilterShowIncludes(line, deps_prefix); + if (!include.empty()) { + string normalized; +#ifdef _WIN32 + if (!normalizer.Normalize(include, &normalized, err)) + return false; +#else + // TODO: should this make the path relative to cwd? + normalized = include; + uint64_t slash_bits; + if (!CanonicalizePath(&normalized, &slash_bits, err)) + return false; +#endif + if (!IsSystemInclude(normalized)) + includes_.insert(normalized); + } else if (FilterInputFilename(line)) { + // Drop it. + // TODO: if we support compiling multiple output files in a single + // cl.exe invocation, we should stash the filename. + } else { + filtered_output->append(line); + filtered_output->append("\n"); + } + + if (end < output.size() && output[end] == '\r') + ++end; + if (end < output.size() && output[end] == '\n') + ++end; + start = end; + } + + return true; +} diff --git a/src/3rdparty/ninja/src/clparser.h b/src/3rdparty/ninja/src/clparser.h new file mode 100644 index 00000000000..e597e7ebc2c --- /dev/null +++ b/src/3rdparty/ninja/src/clparser.h @@ -0,0 +1,52 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_CLPARSER_H_ +#define NINJA_CLPARSER_H_ + +#include +#include +using namespace std; + +/// Visual Studio's cl.exe requires some massaging to work with Ninja; +/// for example, it emits include information on stderr in a funny +/// format when building with /showIncludes. This class parses this +/// output. +struct CLParser { + /// Parse a line of cl.exe output and extract /showIncludes info. + /// If a dependency is extracted, returns a nonempty string. + /// Exposed for testing. + static string FilterShowIncludes(const string& line, + const string& deps_prefix); + + /// Return true if a mentioned include file is a system path. + /// Filtering these out reduces dependency information considerably. + static bool IsSystemInclude(string path); + + /// Parse a line of cl.exe output and return true if it looks like + /// it's printing an input filename. This is a heuristic but it appears + /// to be the best we can do. + /// Exposed for testing. + static bool FilterInputFilename(string line); + + /// Parse the full output of cl, filling filtered_output with the text that + /// should be printed (if any). Returns true on success, or false with err + /// filled. output must not be the same object as filtered_object. + bool Parse(const string& output, const string& deps_prefix, + string* filtered_output, string* err); + + set includes_; +}; + +#endif // NINJA_CLPARSER_H_ diff --git a/src/3rdparty/ninja/src/clparser_perftest.cc b/src/3rdparty/ninja/src/clparser_perftest.cc new file mode 100644 index 00000000000..7ac52302b1c --- /dev/null +++ b/src/3rdparty/ninja/src/clparser_perftest.cc @@ -0,0 +1,157 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "clparser.h" +#include "metrics.h" + +int main(int argc, char* argv[]) { + // Output of /showIncludes from #include + string perf_testdata = + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\iostream\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\istream\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\ostream\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\ios\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xlocnum\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\climits\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\yvals.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xkeycheck.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\crtdefs.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\sal.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\ConcurrencySal.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vadefs.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\use_ansi.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\limits.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cmath\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\math.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xtgmath.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xtr1common\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cstdlib\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\stdlib.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_malloc.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_search.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\stddef.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wstdlib.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cstdio\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\stdio.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wstdio.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_stdio_config.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\streambuf\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xiosbase\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xlocale\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cstring\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\string.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_memory.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_memcpy_s.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\errno.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime_string.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wstring.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\stdexcept\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\exception\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\type_traits\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xstddef\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cstddef\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\initializer_list\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\malloc.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime_exception.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\eh.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_terminate.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xstring\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xmemory0\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cstdint\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\stdint.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\limits\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\ymath.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cfloat\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\float.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cwchar\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\wchar.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wconio.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wctype.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wdirect.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wio.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_share.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wprocess.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\corecrt_wtime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\sys/stat.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\sys/types.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\new\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime_new.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xutility\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\utility\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\iosfwd\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\crtdbg.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime_new_debug.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xatomic0.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\intrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\setjmp.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\immintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\wmmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\nmmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\smmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\tmmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\pmmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\emmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xmmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\mmintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\ammintrin.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\mm3dnow.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\typeinfo\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime_typeinfo.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\vcruntime.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xlocinfo\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xlocinfo.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\ctype.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\locale.h\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\xfacet\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\system_error\r\n" + "Note: including file: C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE\\cerrno\r\n" + "Note: including file: C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt\\share.h\r\n"; + + for (int limit = 1 << 10; limit < (1<<20); limit *= 2) { + int64_t start = GetTimeMillis(); + for (int rep = 0; rep < limit; ++rep) { + string output; + string err; + + CLParser parser; + if (!parser.Parse(perf_testdata, "", &output, &err)) { + printf("%s\n", err.c_str()); + return 1; + } + } + int64_t end = GetTimeMillis(); + + if (end - start > 2000) { + int delta_ms = (int)(end - start); + printf("Parse %d times in %dms avg %.1fus\n", + limit, delta_ms, float(delta_ms * 1000) / limit); + break; + } + } + + return 0; +} diff --git a/src/3rdparty/ninja/src/clparser_test.cc b/src/3rdparty/ninja/src/clparser_test.cc new file mode 100644 index 00000000000..1549ab1cb95 --- /dev/null +++ b/src/3rdparty/ninja/src/clparser_test.cc @@ -0,0 +1,117 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "clparser.h" + +#include "test.h" +#include "util.h" + +TEST(CLParserTest, ShowIncludes) { + ASSERT_EQ("", CLParser::FilterShowIncludes("", "")); + + ASSERT_EQ("", CLParser::FilterShowIncludes("Sample compiler output", "")); + ASSERT_EQ("c:\\Some Files\\foobar.h", + CLParser::FilterShowIncludes("Note: including file: " + "c:\\Some Files\\foobar.h", "")); + ASSERT_EQ("c:\\initspaces.h", + CLParser::FilterShowIncludes("Note: including file: " + "c:\\initspaces.h", "")); + ASSERT_EQ("c:\\initspaces.h", + CLParser::FilterShowIncludes("Non-default prefix: inc file: " + "c:\\initspaces.h", + "Non-default prefix: inc file:")); +} + +TEST(CLParserTest, FilterInputFilename) { + ASSERT_TRUE(CLParser::FilterInputFilename("foobar.cc")); + ASSERT_TRUE(CLParser::FilterInputFilename("foo bar.cc")); + ASSERT_TRUE(CLParser::FilterInputFilename("baz.c")); + ASSERT_TRUE(CLParser::FilterInputFilename("FOOBAR.CC")); + + ASSERT_FALSE(CLParser::FilterInputFilename( + "src\\cl_helper.cc(166) : fatal error C1075: end " + "of file found ...")); +} + +TEST(CLParserTest, ParseSimple) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "foo\r\n" + "Note: inc file prefix: foo.h\r\n" + "bar\r\n", + "Note: inc file prefix:", &output, &err)); + + ASSERT_EQ("foo\nbar\n", output); + ASSERT_EQ(1u, parser.includes_.size()); + ASSERT_EQ("foo.h", *parser.includes_.begin()); +} + +TEST(CLParserTest, ParseFilenameFilter) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "foo.cc\r\n" + "cl: warning\r\n", + "", &output, &err)); + ASSERT_EQ("cl: warning\n", output); +} + +TEST(CLParserTest, ParseSystemInclude) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "Note: including file: c:\\Program Files\\foo.h\r\n" + "Note: including file: d:\\Microsoft Visual Studio\\bar.h\r\n" + "Note: including file: path.h\r\n", + "", &output, &err)); + // We should have dropped the first two includes because they look like + // system headers. + ASSERT_EQ("", output); + ASSERT_EQ(1u, parser.includes_.size()); + ASSERT_EQ("path.h", *parser.includes_.begin()); +} + +TEST(CLParserTest, DuplicatedHeader) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "Note: including file: foo.h\r\n" + "Note: including file: bar.h\r\n" + "Note: including file: foo.h\r\n", + "", &output, &err)); + // We should have dropped one copy of foo.h. + ASSERT_EQ("", output); + ASSERT_EQ(2u, parser.includes_.size()); +} + +TEST(CLParserTest, DuplicatedHeaderPathConverted) { + CLParser parser; + string output, err; + + // This isn't inline in the Parse() call below because the #ifdef in + // a macro expansion would confuse MSVC2013's preprocessor. + const char kInput[] = + "Note: including file: sub/./foo.h\r\n" + "Note: including file: bar.h\r\n" +#ifdef _WIN32 + "Note: including file: sub\\foo.h\r\n"; +#else + "Note: including file: sub/foo.h\r\n"; +#endif + ASSERT_TRUE(parser.Parse(kInput, "", &output, &err)); + // We should have dropped one copy of foo.h. + ASSERT_EQ("", output); + ASSERT_EQ(2u, parser.includes_.size()); +} diff --git a/src/3rdparty/ninja/src/debug_flags.cc b/src/3rdparty/ninja/src/debug_flags.cc new file mode 100644 index 00000000000..44b14c483b5 --- /dev/null +++ b/src/3rdparty/ninja/src/debug_flags.cc @@ -0,0 +1,21 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +bool g_explaining = false; + +bool g_keep_depfile = false; + +bool g_keep_rsp = false; + +bool g_experimental_statcache = true; diff --git a/src/3rdparty/ninja/src/debug_flags.h b/src/3rdparty/ninja/src/debug_flags.h new file mode 100644 index 00000000000..e08a43b438d --- /dev/null +++ b/src/3rdparty/ninja/src/debug_flags.h @@ -0,0 +1,33 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_EXPLAIN_H_ +#define NINJA_EXPLAIN_H_ + +#include + +#define EXPLAIN(fmt, ...) { \ + if (g_explaining) \ + fprintf(stderr, "ninja explain: " fmt "\n", __VA_ARGS__); \ +} + +extern bool g_explaining; + +extern bool g_keep_depfile; + +extern bool g_keep_rsp; + +extern bool g_experimental_statcache; + +#endif // NINJA_EXPLAIN_H_ diff --git a/src/3rdparty/ninja/src/depfile_parser.cc b/src/3rdparty/ninja/src/depfile_parser.cc new file mode 100644 index 00000000000..7cee8921092 --- /dev/null +++ b/src/3rdparty/ninja/src/depfile_parser.cc @@ -0,0 +1,242 @@ +/* Generated by re2c 0.13.5 */ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "depfile_parser.h" + +// A note on backslashes in Makefiles, from reading the docs: +// Backslash-newline is the line continuation character. +// Backslash-# escapes a # (otherwise meaningful as a comment start). +// Backslash-% escapes a % (otherwise meaningful as a special). +// Finally, quoting the GNU manual, "Backslashes that are not in danger +// of quoting ‘%’ characters go unmolested." +// How do you end a line with a backslash? The netbsd Make docs suggest +// reading the result of a shell command echoing a backslash! +// +// Rather than implement all of above, we do a simpler thing here: +// Backslashes escape a set of characters (see "escapes" defined below), +// otherwise they are passed through verbatim. +// If anyone actually has depfiles that rely on the more complicated +// behavior we can adjust this. +bool DepfileParser::Parse(string* content, string* err) { + // in: current parser input point. + // end: end of input. + // parsing_targets: whether we are parsing targets or dependencies. + char* in = &(*content)[0]; + char* end = in + content->size(); + bool parsing_targets = true; + while (in < end) { + // out: current output point (typically same as in, but can fall behind + // as we de-escape backslashes). + char* out = in; + // filename: start of the current parsed filename. + char* filename = out; + for (;;) { + // start: beginning of the current parsed span. + const char* start = in; + + { + unsigned char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 0, 0, 0, + 128, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 128, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 128, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + }; + + yych = *in; + if (yych <= '=') { + if (yych <= '$') { + if (yych <= ' ') { + if (yych <= 0x00) goto yy7; + goto yy9; + } else { + if (yych <= '!') goto yy5; + if (yych <= '#') goto yy9; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '\'') goto yy9; + if (yych <= ')') goto yy5; + goto yy9; + } else { + if (yych <= ':') goto yy5; + if (yych <= '<') goto yy9; + goto yy5; + } + } + } else { + if (yych <= '_') { + if (yych <= '[') { + if (yych <= '?') goto yy9; + if (yych <= 'Z') goto yy5; + goto yy9; + } else { + if (yych <= '\\') goto yy2; + if (yych <= '^') goto yy9; + goto yy5; + } + } else { + if (yych <= '|') { + if (yych <= '`') goto yy9; + if (yych <= '{') goto yy5; + goto yy9; + } else { + if (yych == 0x7F) goto yy9; + goto yy5; + } + } + } +yy2: + ++in; + if ((yych = *in) <= '"') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy3; + if (yych != '\n') goto yy14; + } else { + if (yych <= '\r') goto yy3; + if (yych == ' ') goto yy16; + goto yy14; + } + } else { + if (yych <= 'Z') { + if (yych <= '#') goto yy16; + if (yych == '*') goto yy16; + goto yy14; + } else { + if (yych <= '\\') goto yy16; + if (yych == '|') goto yy16; + goto yy14; + } + } +yy3: + { + // For any other character (e.g. whitespace), swallow it here, + // allowing the outer logic to loop around again. + break; + } +yy4: + yych = *++in; + if (yych == '$') goto yy12; + goto yy3; +yy5: + ++in; + yych = *in; + goto yy11; +yy6: + { + // Got a span of plain text. + int len = (int)(in - start); + // Need to shift it over if we're overwriting backslashes. + if (out < start) + memmove(out, start, len); + out += len; + continue; + } +yy7: + ++in; + { + break; + } +yy9: + yych = *++in; + goto yy3; +yy10: + ++in; + yych = *in; +yy11: + if (yybm[0+yych] & 128) { + goto yy10; + } + goto yy6; +yy12: + ++in; + { + // De-escape dollar character. + *out++ = '$'; + continue; + } +yy14: + ++in; + { + // Let backslash before other characters through verbatim. + *out++ = '\\'; + *out++ = yych; + continue; + } +yy16: + ++in; + { + // De-escape backslashed character. + *out++ = yych; + continue; + } + } + + } + + int len = (int)(out - filename); + const bool is_target = parsing_targets; + if (len > 0 && filename[len - 1] == ':') { + len--; // Strip off trailing colon, if any. + parsing_targets = false; + } + + if (len == 0) + continue; + + if (!is_target) { + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + *err = "depfile has multiple output paths"; + return false; + } + } + if (parsing_targets) { + *err = "expected ':' in depfile"; + return false; + } + return true; +} diff --git a/src/3rdparty/ninja/src/depfile_parser.h b/src/3rdparty/ninja/src/depfile_parser.h new file mode 100644 index 00000000000..1e6ebb57950 --- /dev/null +++ b/src/3rdparty/ninja/src/depfile_parser.h @@ -0,0 +1,35 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_DEPFILE_PARSER_H_ +#define NINJA_DEPFILE_PARSER_H_ + +#include +#include +using namespace std; + +#include "string_piece.h" + +/// Parser for the dependency information emitted by gcc's -M flags. +struct DepfileParser { + /// Parse an input file. Input must be NUL-terminated. + /// Warning: may mutate the content in-place and parsed StringPieces are + /// pointers within it. + bool Parse(string* content, string* err); + + StringPiece out_; + vector ins_; +}; + +#endif // NINJA_DEPFILE_PARSER_H_ diff --git a/src/3rdparty/ninja/src/depfile_parser.in.cc b/src/3rdparty/ninja/src/depfile_parser.in.cc new file mode 100644 index 00000000000..98c1621d466 --- /dev/null +++ b/src/3rdparty/ninja/src/depfile_parser.in.cc @@ -0,0 +1,120 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "depfile_parser.h" + +// A note on backslashes in Makefiles, from reading the docs: +// Backslash-newline is the line continuation character. +// Backslash-# escapes a # (otherwise meaningful as a comment start). +// Backslash-% escapes a % (otherwise meaningful as a special). +// Finally, quoting the GNU manual, "Backslashes that are not in danger +// of quoting ‘%’ characters go unmolested." +// How do you end a line with a backslash? The netbsd Make docs suggest +// reading the result of a shell command echoing a backslash! +// +// Rather than implement all of above, we do a simpler thing here: +// Backslashes escape a set of characters (see "escapes" defined below), +// otherwise they are passed through verbatim. +// If anyone actually has depfiles that rely on the more complicated +// behavior we can adjust this. +bool DepfileParser::Parse(string* content, string* err) { + // in: current parser input point. + // end: end of input. + // parsing_targets: whether we are parsing targets or dependencies. + char* in = &(*content)[0]; + char* end = in + content->size(); + bool parsing_targets = true; + while (in < end) { + // out: current output point (typically same as in, but can fall behind + // as we de-escape backslashes). + char* out = in; + // filename: start of the current parsed filename. + char* filename = out; + for (;;) { + // start: beginning of the current parsed span. + const char* start = in; + /*!re2c + re2c:define:YYCTYPE = "unsigned char"; + re2c:define:YYCURSOR = in; + re2c:define:YYLIMIT = end; + + re2c:yyfill:enable = 0; + + re2c:indent:top = 2; + re2c:indent:string = " "; + + nul = "\000"; + escape = [ \\#*[|]; + + '\\' escape { + // De-escape backslashed character. + *out++ = yych; + continue; + } + '$$' { + // De-escape dollar character. + *out++ = '$'; + continue; + } + '\\' [^\000\r\n] { + // Let backslash before other characters through verbatim. + *out++ = '\\'; + *out++ = yych; + continue; + } + [a-zA-Z0-9+,/_:.~()}{@=!\x80-\xFF-]+ { + // Got a span of plain text. + int len = (int)(in - start); + // Need to shift it over if we're overwriting backslashes. + if (out < start) + memmove(out, start, len); + out += len; + continue; + } + nul { + break; + } + [^] { + // For any other character (e.g. whitespace), swallow it here, + // allowing the outer logic to loop around again. + break; + } + */ + } + + int len = (int)(out - filename); + const bool is_target = parsing_targets; + if (len > 0 && filename[len - 1] == ':') { + len--; // Strip off trailing colon, if any. + parsing_targets = false; + } + + if (len == 0) + continue; + + if (!is_target) { + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + *err = "depfile has multiple output paths"; + return false; + } + } + if (parsing_targets) { + *err = "expected ':' in depfile"; + return false; + } + return true; +} diff --git a/src/3rdparty/ninja/src/depfile_parser_perftest.cc b/src/3rdparty/ninja/src/depfile_parser_perftest.cc new file mode 100644 index 00000000000..b21522168df --- /dev/null +++ b/src/3rdparty/ninja/src/depfile_parser_perftest.cc @@ -0,0 +1,77 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "depfile_parser.h" +#include "util.h" +#include "metrics.h" + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("usage: %s \n", argv[0]); + return 1; + } + + vector times; + for (int i = 1; i < argc; ++i) { + const char* filename = argv[i]; + + for (int limit = 1 << 10; limit < (1<<20); limit *= 2) { + int64_t start = GetTimeMillis(); + for (int rep = 0; rep < limit; ++rep) { + string buf; + string err; + if (ReadFile(filename, &buf, &err) < 0) { + printf("%s: %s\n", filename, err.c_str()); + return 1; + } + + DepfileParser parser; + if (!parser.Parse(&buf, &err)) { + printf("%s: %s\n", filename, err.c_str()); + return 1; + } + } + int64_t end = GetTimeMillis(); + + if (end - start > 100) { + int delta = (int)(end - start); + float time = delta*1000 / (float)limit; + printf("%s: %.1fus\n", filename, time); + times.push_back(time); + break; + } + } + } + + if (!times.empty()) { + float min = times[0]; + float max = times[0]; + float total = 0; + for (size_t i = 0; i < times.size(); ++i) { + total += times[i]; + if (times[i] < min) + min = times[i]; + else if (times[i] > max) + max = times[i]; + } + + printf("min %.1fus max %.1fus avg %.1fus\n", + min, max, total / times.size()); + } + + return 0; +} diff --git a/src/3rdparty/ninja/src/depfile_parser_test.cc b/src/3rdparty/ninja/src/depfile_parser_test.cc new file mode 100644 index 00000000000..ee798f82394 --- /dev/null +++ b/src/3rdparty/ninja/src/depfile_parser_test.cc @@ -0,0 +1,157 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "depfile_parser.h" + +#include "test.h" + +struct DepfileParserTest : public testing::Test { + bool Parse(const char* input, string* err); + + DepfileParser parser_; + string input_; +}; + +bool DepfileParserTest::Parse(const char* input, string* err) { + input_ = input; + return parser_.Parse(&input_, err); +} + +TEST_F(DepfileParserTest, Basic) { + string err; + EXPECT_TRUE(Parse( +"build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h\n", + &err)); + ASSERT_EQ("", err); + EXPECT_EQ("build/ninja.o", parser_.out_.AsString()); + EXPECT_EQ(4u, parser_.ins_.size()); +} + +TEST_F(DepfileParserTest, EarlyNewlineAndWhitespace) { + string err; + EXPECT_TRUE(Parse( +" \\\n" +" out: in\n", + &err)); + ASSERT_EQ("", err); +} + +TEST_F(DepfileParserTest, Continuation) { + string err; + EXPECT_TRUE(Parse( +"foo.o: \\\n" +" bar.h baz.h\n", + &err)); + ASSERT_EQ("", err); + EXPECT_EQ("foo.o", parser_.out_.AsString()); + EXPECT_EQ(2u, parser_.ins_.size()); +} + +TEST_F(DepfileParserTest, CarriageReturnContinuation) { + string err; + EXPECT_TRUE(Parse( +"foo.o: \\\r\n" +" bar.h baz.h\r\n", + &err)); + ASSERT_EQ("", err); + EXPECT_EQ("foo.o", parser_.out_.AsString()); + EXPECT_EQ(2u, parser_.ins_.size()); +} + +TEST_F(DepfileParserTest, BackSlashes) { + string err; + EXPECT_TRUE(Parse( +"Project\\Dir\\Build\\Release8\\Foo\\Foo.res : \\\n" +" Dir\\Library\\Foo.rc \\\n" +" Dir\\Library\\Version\\Bar.h \\\n" +" Dir\\Library\\Foo.ico \\\n" +" Project\\Thing\\Bar.tlb \\\n", + &err)); + ASSERT_EQ("", err); + EXPECT_EQ("Project\\Dir\\Build\\Release8\\Foo\\Foo.res", + parser_.out_.AsString()); + EXPECT_EQ(4u, parser_.ins_.size()); +} + +TEST_F(DepfileParserTest, Spaces) { + string err; + EXPECT_TRUE(Parse( +"a\\ bc\\ def: a\\ b c d", + &err)); + ASSERT_EQ("", err); + EXPECT_EQ("a bc def", + parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("a b", + parser_.ins_[0].AsString()); + EXPECT_EQ("c", + parser_.ins_[1].AsString()); + EXPECT_EQ("d", + parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, Escapes) { + // Put backslashes before a variety of characters, see which ones make + // it through. + string err; + EXPECT_TRUE(Parse( +"\\!\\@\\#$$\\%\\^\\&\\\\:", + &err)); + ASSERT_EQ("", err); + EXPECT_EQ("\\!\\@#$\\%\\^\\&\\", + parser_.out_.AsString()); + ASSERT_EQ(0u, parser_.ins_.size()); +} + +TEST_F(DepfileParserTest, SpecialChars) { + // See filenames like istreambuf.iterator_op!= in + // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/ + string err; + EXPECT_TRUE(Parse( +"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n" +" en@quot.header~ t+t-x!=1 \n" +" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\n" +" Fu\303\244ball", + &err)); + ASSERT_EQ("", err); + EXPECT_EQ("C:/Program Files (x86)/Microsoft crtdefs.h", + parser_.out_.AsString()); + ASSERT_EQ(4u, parser_.ins_.size()); + EXPECT_EQ("en@quot.header~", + parser_.ins_[0].AsString()); + EXPECT_EQ("t+t-x!=1", + parser_.ins_[1].AsString()); + EXPECT_EQ("openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif", + parser_.ins_[2].AsString()); + EXPECT_EQ("Fu\303\244ball", + parser_.ins_[3].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMultipleOutputs) { + // check that multiple duplicate targets are properly unified + string err; + EXPECT_TRUE(Parse("foo foo: x y z", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, RejectMultipleDifferentOutputs) { + // check that multiple different outputs are rejected by the parser + string err; + EXPECT_FALSE(Parse("foo bar: x y z", &err)); + ASSERT_EQ("depfile has multiple output paths", err); +} diff --git a/src/3rdparty/ninja/src/deps_log.cc b/src/3rdparty/ninja/src/deps_log.cc new file mode 100644 index 00000000000..89c60232b7b --- /dev/null +++ b/src/3rdparty/ninja/src/deps_log.cc @@ -0,0 +1,410 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "deps_log.h" + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "graph.h" +#include "metrics.h" +#include "state.h" +#include "util.h" + +// The version is stored as 4 bytes after the signature and also serves as a +// byte order mark. Signature and version combined are 16 bytes long. +const char kFileSignature[] = "# ninjadeps\n"; +const int kCurrentVersion = 3; + +// Record size is currently limited to less than the full 32 bit, due to +// internal buffers having to have this size. +const unsigned kMaxRecordSize = (1 << 19) - 1; + +DepsLog::~DepsLog() { + Close(); +} + +bool DepsLog::OpenForWrite(const string& path, string* err) { + if (needs_recompaction_) { + if (!Recompact(path, err)) + return false; + } + + file_ = fopen(path.c_str(), "ab"); + if (!file_) { + *err = strerror(errno); + return false; + } + // Set the buffer size to this and flush the file buffer after every record + // to make sure records aren't written partially. + setvbuf(file_, NULL, _IOFBF, kMaxRecordSize + 1); + SetCloseOnExec(fileno(file_)); + + // Opening a file in append mode doesn't set the file pointer to the file's + // end on Windows. Do that explicitly. + fseek(file_, 0, SEEK_END); + + if (ftell(file_) == 0) { + if (fwrite(kFileSignature, sizeof(kFileSignature) - 1, 1, file_) < 1) { + *err = strerror(errno); + return false; + } + if (fwrite(&kCurrentVersion, 4, 1, file_) < 1) { + *err = strerror(errno); + return false; + } + } + if (fflush(file_) != 0) { + *err = strerror(errno); + return false; + } + return true; +} + +bool DepsLog::RecordDeps(Node* node, TimeStamp mtime, + const vector& nodes) { + return RecordDeps(node, mtime, nodes.size(), + nodes.empty() ? NULL : (Node**)&nodes.front()); +} + +bool DepsLog::RecordDeps(Node* node, TimeStamp mtime, + int node_count, Node** nodes) { + // Track whether there's any new data to be recorded. + bool made_change = false; + + // Assign ids to all nodes that are missing one. + if (node->id() < 0) { + if (!RecordId(node)) + return false; + made_change = true; + } + for (int i = 0; i < node_count; ++i) { + if (nodes[i]->id() < 0) { + if (!RecordId(nodes[i])) + return false; + made_change = true; + } + } + + // See if the new data is different than the existing data, if any. + if (!made_change) { + Deps* deps = GetDeps(node); + if (!deps || + deps->mtime != mtime || + deps->node_count != node_count) { + made_change = true; + } else { + for (int i = 0; i < node_count; ++i) { + if (deps->nodes[i] != nodes[i]) { + made_change = true; + break; + } + } + } + } + + // Don't write anything if there's no new info. + if (!made_change) + return true; + + // Update on-disk representation. + unsigned size = 4 * (1 + 1 + node_count); + if (size > kMaxRecordSize) { + errno = ERANGE; + return false; + } + size |= 0x80000000; // Deps record: set high bit. + if (fwrite(&size, 4, 1, file_) < 1) + return false; + int id = node->id(); + if (fwrite(&id, 4, 1, file_) < 1) + return false; + int timestamp = mtime; + if (fwrite(×tamp, 4, 1, file_) < 1) + return false; + for (int i = 0; i < node_count; ++i) { + id = nodes[i]->id(); + if (fwrite(&id, 4, 1, file_) < 1) + return false; + } + if (fflush(file_) != 0) + return false; + + // Update in-memory representation. + Deps* deps = new Deps(mtime, node_count); + for (int i = 0; i < node_count; ++i) + deps->nodes[i] = nodes[i]; + UpdateDeps(node->id(), deps); + + return true; +} + +void DepsLog::Close() { + if (file_) + fclose(file_); + file_ = NULL; +} + +bool DepsLog::Load(const string& path, State* state, string* err) { + METRIC_RECORD(".ninja_deps load"); + char buf[kMaxRecordSize + 1]; + FILE* f = fopen(path.c_str(), "rb"); + if (!f) { + if (errno == ENOENT) + return true; + *err = strerror(errno); + return false; + } + + bool valid_header = true; + int version = 0; + if (!fgets(buf, sizeof(buf), f) || fread(&version, 4, 1, f) < 1) + valid_header = false; + // Note: For version differences, this should migrate to the new format. + // But the v1 format could sometimes (rarely) end up with invalid data, so + // don't migrate v1 to v3 to force a rebuild. (v2 only existed for a few days, + // and there was no release with it, so pretend that it never happened.) + if (!valid_header || strcmp(buf, kFileSignature) != 0 || + version != kCurrentVersion) { + if (version == 1) + *err = "deps log version change; rebuilding"; + else + *err = "bad deps log signature or version; starting over"; + fclose(f); + unlink(path.c_str()); + // Don't report this as a failure. An empty deps log will cause + // us to rebuild the outputs anyway. + return true; + } + + long offset; + bool read_failed = false; + int unique_dep_record_count = 0; + int total_dep_record_count = 0; + for (;;) { + offset = ftell(f); + + unsigned size; + if (fread(&size, 4, 1, f) < 1) { + if (!feof(f)) + read_failed = true; + break; + } + bool is_deps = (size >> 31) != 0; + size = size & 0x7FFFFFFF; + + if (fread(buf, size, 1, f) < 1 || size > kMaxRecordSize) { + read_failed = true; + break; + } + + if (is_deps) { + assert(size % 4 == 0); + int* deps_data = reinterpret_cast(buf); + int out_id = deps_data[0]; + int mtime = deps_data[1]; + deps_data += 2; + int deps_count = (size / 4) - 2; + + Deps* deps = new Deps(mtime, deps_count); + for (int i = 0; i < deps_count; ++i) { + assert(deps_data[i] < (int)nodes_.size()); + assert(nodes_[deps_data[i]]); + deps->nodes[i] = nodes_[deps_data[i]]; + } + + total_dep_record_count++; + if (!UpdateDeps(out_id, deps)) + ++unique_dep_record_count; + } else { + int path_size = size - 4; + assert(path_size > 0); // CanonicalizePath() rejects empty paths. + // There can be up to 3 bytes of padding. + if (buf[path_size - 1] == '\0') --path_size; + if (buf[path_size - 1] == '\0') --path_size; + if (buf[path_size - 1] == '\0') --path_size; + StringPiece subpath(buf, path_size); + // It is not necessary to pass in a correct slash_bits here. It will + // either be a Node that's in the manifest (in which case it will already + // have a correct slash_bits that GetNode will look up), or it is an + // implicit dependency from a .d which does not affect the build command + // (and so need not have its slashes maintained). + Node* node = state->GetNode(subpath, 0); + + // Check that the expected index matches the actual index. This can only + // happen if two ninja processes write to the same deps log concurrently. + // (This uses unary complement to make the checksum look less like a + // dependency record entry.) + unsigned checksum = *reinterpret_cast(buf + size - 4); + int expected_id = ~checksum; + int id = nodes_.size(); + if (id != expected_id) { + read_failed = true; + break; + } + + assert(node->id() < 0); + node->set_id(id); + nodes_.push_back(node); + } + } + + if (read_failed) { + // An error occurred while loading; try to recover by truncating the + // file to the last fully-read record. + if (ferror(f)) { + *err = strerror(ferror(f)); + } else { + *err = "premature end of file"; + } + fclose(f); + + if (!Truncate(path, offset, err)) + return false; + + // The truncate succeeded; we'll just report the load error as a + // warning because the build can proceed. + *err += "; recovering"; + return true; + } + + fclose(f); + + // Rebuild the log if there are too many dead records. + int kMinCompactionEntryCount = 1000; + int kCompactionRatio = 3; + if (total_dep_record_count > kMinCompactionEntryCount && + total_dep_record_count > unique_dep_record_count * kCompactionRatio) { + needs_recompaction_ = true; + } + + return true; +} + +DepsLog::Deps* DepsLog::GetDeps(Node* node) { + // Abort if the node has no id (never referenced in the deps) or if + // there's no deps recorded for the node. + if (node->id() < 0 || node->id() >= (int)deps_.size()) + return NULL; + return deps_[node->id()]; +} + +bool DepsLog::Recompact(const string& path, string* err) { + METRIC_RECORD(".ninja_deps recompact"); + + Close(); + string temp_path = path + ".recompact"; + + // OpenForWrite() opens for append. Make sure it's not appending to a + // left-over file from a previous recompaction attempt that crashed somehow. + unlink(temp_path.c_str()); + + DepsLog new_log; + if (!new_log.OpenForWrite(temp_path, err)) + return false; + + // Clear all known ids so that new ones can be reassigned. The new indices + // will refer to the ordering in new_log, not in the current log. + for (vector::iterator i = nodes_.begin(); i != nodes_.end(); ++i) + (*i)->set_id(-1); + + // Write out all deps again. + for (int old_id = 0; old_id < (int)deps_.size(); ++old_id) { + Deps* deps = deps_[old_id]; + if (!deps) continue; // If nodes_[old_id] is a leaf, it has no deps. + + if (!IsDepsEntryLiveFor(nodes_[old_id])) + continue; + + if (!new_log.RecordDeps(nodes_[old_id], deps->mtime, + deps->node_count, deps->nodes)) { + new_log.Close(); + return false; + } + } + + new_log.Close(); + + // All nodes now have ids that refer to new_log, so steal its data. + deps_.swap(new_log.deps_); + nodes_.swap(new_log.nodes_); + + if (unlink(path.c_str()) < 0) { + *err = strerror(errno); + return false; + } + + if (rename(temp_path.c_str(), path.c_str()) < 0) { + *err = strerror(errno); + return false; + } + + return true; +} + +bool DepsLog::IsDepsEntryLiveFor(Node* node) { + // Skip entries that don't have in-edges or whose edges don't have a + // "deps" attribute. They were in the deps log from previous builds, but + // the the files they were for were removed from the build and their deps + // entries are no longer needed. + // (Without the check for "deps", a chain of two or more nodes that each + // had deps wouldn't be collected in a single recompaction.) + return node->in_edge() && !node->in_edge()->GetBinding("deps").empty(); +} + +bool DepsLog::UpdateDeps(int out_id, Deps* deps) { + if (out_id >= (int)deps_.size()) + deps_.resize(out_id + 1); + + bool delete_old = deps_[out_id] != NULL; + if (delete_old) + delete deps_[out_id]; + deps_[out_id] = deps; + return delete_old; +} + +bool DepsLog::RecordId(Node* node) { + int path_size = node->path().size(); + int padding = (4 - path_size % 4) % 4; // Pad path to 4 byte boundary. + + unsigned size = path_size + padding + 4; + if (size > kMaxRecordSize) { + errno = ERANGE; + return false; + } + if (fwrite(&size, 4, 1, file_) < 1) + return false; + if (fwrite(node->path().data(), path_size, 1, file_) < 1) { + assert(node->path().size() > 0); + return false; + } + if (padding && fwrite("\0\0", padding, 1, file_) < 1) + return false; + int id = nodes_.size(); + unsigned checksum = ~(unsigned)id; + if (fwrite(&checksum, 4, 1, file_) < 1) + return false; + if (fflush(file_) != 0) + return false; + + node->set_id(id); + nodes_.push_back(node); + + return true; +} diff --git a/src/3rdparty/ninja/src/deps_log.h b/src/3rdparty/ninja/src/deps_log.h new file mode 100644 index 00000000000..cec0257ceff --- /dev/null +++ b/src/3rdparty/ninja/src/deps_log.h @@ -0,0 +1,121 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_DEPS_LOG_H_ +#define NINJA_DEPS_LOG_H_ + +#include +#include +using namespace std; + +#include + +#include "timestamp.h" + +struct Node; +struct State; + +/// As build commands run they can output extra dependency information +/// (e.g. header dependencies for C source) dynamically. DepsLog collects +/// that information at build time and uses it for subsequent builds. +/// +/// The on-disk format is based on two primary design constraints: +/// - it must be written to as a stream (during the build, which may be +/// interrupted); +/// - it can be read all at once on startup. (Alternative designs, where +/// it contains indexing information, were considered and discarded as +/// too complicated to implement; if the file is small than reading it +/// fully on startup is acceptable.) +/// Here are some stats from the Windows Chrome dependency files, to +/// help guide the design space. The total text in the files sums to +/// 90mb so some compression is warranted to keep load-time fast. +/// There's about 10k files worth of dependencies that reference about +/// 40k total paths totalling 2mb of unique strings. +/// +/// Based on these stats, here's the current design. +/// The file is structured as version header followed by a sequence of records. +/// Each record is either a path string or a dependency list. +/// Numbering the path strings in file order gives them dense integer ids. +/// A dependency list maps an output id to a list of input ids. +/// +/// Concretely, a record is: +/// four bytes record length, high bit indicates record type +/// (but max record sizes are capped at 512kB) +/// path records contain the string name of the path, followed by up to 3 +/// padding bytes to align on 4 byte boundaries, followed by the +/// one's complement of the expected index of the record (to detect +/// concurrent writes of multiple ninja processes to the log). +/// dependency records are an array of 4-byte integers +/// [output path id, output path mtime, input path id, input path id...] +/// (The mtime is compared against the on-disk output path mtime +/// to verify the stored data is up-to-date.) +/// If two records reference the same output the latter one in the file +/// wins, allowing updates to just be appended to the file. A separate +/// repacking step can run occasionally to remove dead records. +struct DepsLog { + DepsLog() : needs_recompaction_(false), file_(NULL) {} + ~DepsLog(); + + // Writing (build-time) interface. + bool OpenForWrite(const string& path, string* err); + bool RecordDeps(Node* node, TimeStamp mtime, const vector& nodes); + bool RecordDeps(Node* node, TimeStamp mtime, int node_count, Node** nodes); + void Close(); + + // Reading (startup-time) interface. + struct Deps { + Deps(int mtime, int node_count) + : mtime(mtime), node_count(node_count), nodes(new Node*[node_count]) {} + ~Deps() { delete [] nodes; } + int mtime; + int node_count; + Node** nodes; + }; + bool Load(const string& path, State* state, string* err); + Deps* GetDeps(Node* node); + + /// Rewrite the known log entries, throwing away old data. + bool Recompact(const string& path, string* err); + + /// Returns if the deps entry for a node is still reachable from the manifest. + /// + /// The deps log can contain deps entries for files that were built in the + /// past but are no longer part of the manifest. This function returns if + /// this is the case for a given node. This function is slow, don't call + /// it from code that runs on every build. + bool IsDepsEntryLiveFor(Node* node); + + /// Used for tests. + const vector& nodes() const { return nodes_; } + const vector& deps() const { return deps_; } + + private: + // Updates the in-memory representation. Takes ownership of |deps|. + // Returns true if a prior deps record was deleted. + bool UpdateDeps(int out_id, Deps* deps); + // Write a node name record, assigning it an id. + bool RecordId(Node* node); + + bool needs_recompaction_; + FILE* file_; + + /// Maps id -> Node. + vector nodes_; + /// Maps id -> deps of that id. + vector deps_; + + friend struct DepsLogTest; +}; + +#endif // NINJA_DEPS_LOG_H_ diff --git a/src/3rdparty/ninja/src/deps_log_test.cc b/src/3rdparty/ninja/src/deps_log_test.cc new file mode 100644 index 00000000000..89f7be159e0 --- /dev/null +++ b/src/3rdparty/ninja/src/deps_log_test.cc @@ -0,0 +1,479 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "deps_log.h" + +#include +#ifndef _WIN32 +#include +#endif + +#include "graph.h" +#include "util.h" +#include "test.h" + +namespace { + +const char kTestFilename[] = "DepsLogTest-tempfile"; + +struct DepsLogTest : public testing::Test { + virtual void SetUp() { + // In case a crashing test left a stale file behind. + unlink(kTestFilename); + } + virtual void TearDown() { + unlink(kTestFilename); + } +}; + +TEST_F(DepsLogTest, WriteRead) { + State state1; + DepsLog log1; + string err; + EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + { + vector deps; + deps.push_back(state1.GetNode("foo.h", 0)); + deps.push_back(state1.GetNode("bar.h", 0)); + log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps); + + deps.clear(); + deps.push_back(state1.GetNode("foo.h", 0)); + deps.push_back(state1.GetNode("bar2.h", 0)); + log1.RecordDeps(state1.GetNode("out2.o", 0), 2, deps); + + DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0)); + ASSERT_TRUE(log_deps); + ASSERT_EQ(1, log_deps->mtime); + ASSERT_EQ(2, log_deps->node_count); + ASSERT_EQ("foo.h", log_deps->nodes[0]->path()); + ASSERT_EQ("bar.h", log_deps->nodes[1]->path()); + } + + log1.Close(); + + State state2; + DepsLog log2; + EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err)); + ASSERT_EQ("", err); + + ASSERT_EQ(log1.nodes().size(), log2.nodes().size()); + for (int i = 0; i < (int)log1.nodes().size(); ++i) { + Node* node1 = log1.nodes()[i]; + Node* node2 = log2.nodes()[i]; + ASSERT_EQ(i, node1->id()); + ASSERT_EQ(node1->id(), node2->id()); + } + + // Spot-check the entries in log2. + DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out2.o", 0)); + ASSERT_TRUE(log_deps); + ASSERT_EQ(2, log_deps->mtime); + ASSERT_EQ(2, log_deps->node_count); + ASSERT_EQ("foo.h", log_deps->nodes[0]->path()); + ASSERT_EQ("bar2.h", log_deps->nodes[1]->path()); +} + +TEST_F(DepsLogTest, LotsOfDeps) { + const int kNumDeps = 100000; // More than 64k. + + State state1; + DepsLog log1; + string err; + EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + { + vector deps; + for (int i = 0; i < kNumDeps; ++i) { + char buf[32]; + sprintf(buf, "file%d.h", i); + deps.push_back(state1.GetNode(buf, 0)); + } + log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps); + + DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0)); + ASSERT_EQ(kNumDeps, log_deps->node_count); + } + + log1.Close(); + + State state2; + DepsLog log2; + EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err)); + ASSERT_EQ("", err); + + DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out.o", 0)); + ASSERT_EQ(kNumDeps, log_deps->node_count); +} + +// Verify that adding the same deps twice doesn't grow the file. +TEST_F(DepsLogTest, DoubleEntry) { + // Write some deps to the file and grab its size. + int file_size; + { + State state; + DepsLog log; + string err; + EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + vector deps; + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar.h", 0)); + log.RecordDeps(state.GetNode("out.o", 0), 1, deps); + log.Close(); + + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + file_size = (int)st.st_size; + ASSERT_GT(file_size, 0); + } + + // Now reload the file, and readd the same deps. + { + State state; + DepsLog log; + string err; + EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); + + EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + vector deps; + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar.h", 0)); + log.RecordDeps(state.GetNode("out.o", 0), 1, deps); + log.Close(); + + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + int file_size_2 = (int)st.st_size; + ASSERT_EQ(file_size, file_size_2); + } +} + +// Verify that adding the new deps works and can be compacted away. +TEST_F(DepsLogTest, Recompact) { + const char kManifest[] = +"rule cc\n" +" command = cc\n" +" deps = gcc\n" +"build out.o: cc\n" +"build other_out.o: cc\n"; + + // Write some deps to the file and grab its size. + int file_size; + { + State state; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest)); + DepsLog log; + string err; + ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + vector deps; + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar.h", 0)); + log.RecordDeps(state.GetNode("out.o", 0), 1, deps); + + deps.clear(); + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("baz.h", 0)); + log.RecordDeps(state.GetNode("other_out.o", 0), 1, deps); + + log.Close(); + + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + file_size = (int)st.st_size; + ASSERT_GT(file_size, 0); + } + + // Now reload the file, and add slighly different deps. + int file_size_2; + { + State state; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest)); + DepsLog log; + string err; + ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); + + ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + vector deps; + deps.push_back(state.GetNode("foo.h", 0)); + log.RecordDeps(state.GetNode("out.o", 0), 1, deps); + log.Close(); + + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + file_size_2 = (int)st.st_size; + // The file should grow to record the new deps. + ASSERT_GT(file_size_2, file_size); + } + + // Now reload the file, verify the new deps have replaced the old, then + // recompact. + int file_size_3; + { + State state; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest)); + DepsLog log; + string err; + ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); + + Node* out = state.GetNode("out.o", 0); + DepsLog::Deps* deps = log.GetDeps(out); + ASSERT_TRUE(deps); + ASSERT_EQ(1, deps->mtime); + ASSERT_EQ(1, deps->node_count); + ASSERT_EQ("foo.h", deps->nodes[0]->path()); + + Node* other_out = state.GetNode("other_out.o", 0); + deps = log.GetDeps(other_out); + ASSERT_TRUE(deps); + ASSERT_EQ(1, deps->mtime); + ASSERT_EQ(2, deps->node_count); + ASSERT_EQ("foo.h", deps->nodes[0]->path()); + ASSERT_EQ("baz.h", deps->nodes[1]->path()); + + ASSERT_TRUE(log.Recompact(kTestFilename, &err)); + + // The in-memory deps graph should still be valid after recompaction. + deps = log.GetDeps(out); + ASSERT_TRUE(deps); + ASSERT_EQ(1, deps->mtime); + ASSERT_EQ(1, deps->node_count); + ASSERT_EQ("foo.h", deps->nodes[0]->path()); + ASSERT_EQ(out, log.nodes()[out->id()]); + + deps = log.GetDeps(other_out); + ASSERT_TRUE(deps); + ASSERT_EQ(1, deps->mtime); + ASSERT_EQ(2, deps->node_count); + ASSERT_EQ("foo.h", deps->nodes[0]->path()); + ASSERT_EQ("baz.h", deps->nodes[1]->path()); + ASSERT_EQ(other_out, log.nodes()[other_out->id()]); + + // The file should have shrunk a bit for the smaller deps. + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + file_size_3 = (int)st.st_size; + ASSERT_LT(file_size_3, file_size_2); + } + + // Now reload the file and recompact with an empty manifest. The previous + // entries should be removed. + { + State state; + // Intentionally not parsing kManifest here. + DepsLog log; + string err; + ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); + + Node* out = state.GetNode("out.o", 0); + DepsLog::Deps* deps = log.GetDeps(out); + ASSERT_TRUE(deps); + ASSERT_EQ(1, deps->mtime); + ASSERT_EQ(1, deps->node_count); + ASSERT_EQ("foo.h", deps->nodes[0]->path()); + + Node* other_out = state.GetNode("other_out.o", 0); + deps = log.GetDeps(other_out); + ASSERT_TRUE(deps); + ASSERT_EQ(1, deps->mtime); + ASSERT_EQ(2, deps->node_count); + ASSERT_EQ("foo.h", deps->nodes[0]->path()); + ASSERT_EQ("baz.h", deps->nodes[1]->path()); + + ASSERT_TRUE(log.Recompact(kTestFilename, &err)); + + // The previous entries should have been removed. + deps = log.GetDeps(out); + ASSERT_FALSE(deps); + + deps = log.GetDeps(other_out); + ASSERT_FALSE(deps); + + // The .h files pulled in via deps should no longer have ids either. + ASSERT_EQ(-1, state.LookupNode("foo.h")->id()); + ASSERT_EQ(-1, state.LookupNode("baz.h")->id()); + + // The file should have shrunk more. + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + int file_size_4 = (int)st.st_size; + ASSERT_LT(file_size_4, file_size_3); + } +} + +// Verify that invalid file headers cause a new build. +TEST_F(DepsLogTest, InvalidHeader) { + const char *kInvalidHeaders[] = { + "", // Empty file. + "# ninjad", // Truncated first line. + "# ninjadeps\n", // No version int. + "# ninjadeps\n\001\002", // Truncated version int. + "# ninjadeps\n\001\002\003\004" // Invalid version int. + }; + for (size_t i = 0; i < sizeof(kInvalidHeaders) / sizeof(kInvalidHeaders[0]); + ++i) { + FILE* deps_log = fopen(kTestFilename, "wb"); + ASSERT_TRUE(deps_log != NULL); + ASSERT_EQ( + strlen(kInvalidHeaders[i]), + fwrite(kInvalidHeaders[i], 1, strlen(kInvalidHeaders[i]), deps_log)); + ASSERT_EQ(0 ,fclose(deps_log)); + + string err; + DepsLog log; + State state; + ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); + EXPECT_EQ("bad deps log signature or version; starting over", err); + } +} + +// Simulate what happens when loading a truncated log file. +TEST_F(DepsLogTest, Truncated) { + // Create a file with some entries. + { + State state; + DepsLog log; + string err; + EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + vector deps; + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar.h", 0)); + log.RecordDeps(state.GetNode("out.o", 0), 1, deps); + + deps.clear(); + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar2.h", 0)); + log.RecordDeps(state.GetNode("out2.o", 0), 2, deps); + + log.Close(); + } + + // Get the file size. + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + + // Try reloading at truncated sizes. + // Track how many nodes/deps were found; they should decrease with + // smaller sizes. + int node_count = 5; + int deps_count = 2; + for (int size = (int)st.st_size; size > 0; --size) { + string err; + ASSERT_TRUE(Truncate(kTestFilename, size, &err)); + + State state; + DepsLog log; + EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); + if (!err.empty()) { + // At some point the log will be so short as to be unparseable. + break; + } + + ASSERT_GE(node_count, (int)log.nodes().size()); + node_count = log.nodes().size(); + + // Count how many non-NULL deps entries there are. + int new_deps_count = 0; + for (vector::const_iterator i = log.deps().begin(); + i != log.deps().end(); ++i) { + if (*i) + ++new_deps_count; + } + ASSERT_GE(deps_count, new_deps_count); + deps_count = new_deps_count; + } +} + +// Run the truncation-recovery logic. +TEST_F(DepsLogTest, TruncatedRecovery) { + // Create a file with some entries. + { + State state; + DepsLog log; + string err; + EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + vector deps; + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar.h", 0)); + log.RecordDeps(state.GetNode("out.o", 0), 1, deps); + + deps.clear(); + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar2.h", 0)); + log.RecordDeps(state.GetNode("out2.o", 0), 2, deps); + + log.Close(); + } + + // Shorten the file, corrupting the last record. + { + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + string err; + ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err)); + } + + // Load the file again, add an entry. + { + State state; + DepsLog log; + string err; + EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); + ASSERT_EQ("premature end of file; recovering", err); + err.clear(); + + // The truncated entry should've been discarded. + EXPECT_EQ(NULL, log.GetDeps(state.GetNode("out2.o", 0))); + + EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); + ASSERT_EQ("", err); + + // Add a new entry. + vector deps; + deps.push_back(state.GetNode("foo.h", 0)); + deps.push_back(state.GetNode("bar2.h", 0)); + log.RecordDeps(state.GetNode("out2.o", 0), 3, deps); + + log.Close(); + } + + // Load the file a third time to verify appending after a mangled + // entry doesn't break things. + { + State state; + DepsLog log; + string err; + EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); + + // The truncated entry should exist. + DepsLog::Deps* deps = log.GetDeps(state.GetNode("out2.o", 0)); + ASSERT_TRUE(deps); + } +} + +} // anonymous namespace diff --git a/src/3rdparty/ninja/src/disk_interface.cc b/src/3rdparty/ninja/src/disk_interface.cc new file mode 100644 index 00000000000..28530b19d0d --- /dev/null +++ b/src/3rdparty/ninja/src/disk_interface.cc @@ -0,0 +1,264 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "disk_interface.h" + +#include + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include // _mkdir +#endif + +#include "metrics.h" +#include "util.h" + +namespace { + +string DirName(const string& path) { +#ifdef _WIN32 + const char kPathSeparators[] = "\\/"; +#else + const char kPathSeparators[] = "/"; +#endif + string::size_type slash_pos = path.find_last_of(kPathSeparators); + if (slash_pos == string::npos) + return string(); // Nothing to do. + const char* const kEnd = kPathSeparators + strlen(kPathSeparators); + while (slash_pos > 0 && + std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd) + --slash_pos; + return path.substr(0, slash_pos); +} + +int MakeDir(const string& path) { +#ifdef _WIN32 + return _mkdir(path.c_str()); +#else + return mkdir(path.c_str(), 0777); +#endif +} + +#ifdef _WIN32 +TimeStamp TimeStampFromFileTime(const FILETIME& filetime) { + // FILETIME is in 100-nanosecond increments since the Windows epoch. + // We don't much care about epoch correctness but we do want the + // resulting value to fit in an integer. + uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | + ((uint64_t)filetime.dwLowDateTime); + mtime /= 1000000000LL / 100; // 100ns -> s. + mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years). + return (TimeStamp)mtime; +} + +TimeStamp StatSingleFile(const string& path, string* err) { + WIN32_FILE_ATTRIBUTE_DATA attrs; + if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) { + DWORD win_err = GetLastError(); + if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) + return 0; + *err = "GetFileAttributesEx(" + path + "): " + GetLastErrorString(); + return -1; + } + return TimeStampFromFileTime(attrs.ftLastWriteTime); +} + +bool IsWindows7OrLater() { + OSVERSIONINFOEX version_info = + { sizeof(OSVERSIONINFOEX), 6, 1, 0, 0, {0}, 0, 0, 0, 0, 0}; + DWORDLONG comparison = 0; + VER_SET_CONDITION(comparison, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(comparison, VER_MINORVERSION, VER_GREATER_EQUAL); + return VerifyVersionInfo( + &version_info, VER_MAJORVERSION | VER_MINORVERSION, comparison); +} + +bool StatAllFilesInDir(const string& dir, map* stamps, + string* err) { + // FindExInfoBasic is 30% faster than FindExInfoStandard. + static bool can_use_basic_info = IsWindows7OrLater(); + // This is not in earlier SDKs. + const FINDEX_INFO_LEVELS kFindExInfoBasic = + static_cast(1); + FINDEX_INFO_LEVELS level = + can_use_basic_info ? kFindExInfoBasic : FindExInfoStandard; + WIN32_FIND_DATAA ffd; + HANDLE find_handle = FindFirstFileExA((dir + "\\*").c_str(), level, &ffd, + FindExSearchNameMatch, NULL, 0); + + if (find_handle == INVALID_HANDLE_VALUE) { + DWORD win_err = GetLastError(); + if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) + return true; + *err = "FindFirstFileExA(" + dir + "): " + GetLastErrorString(); + return false; + } + do { + string lowername = ffd.cFileName; + transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower); + stamps->insert(make_pair(lowername, + TimeStampFromFileTime(ffd.ftLastWriteTime))); + } while (FindNextFileA(find_handle, &ffd)); + FindClose(find_handle); + return true; +} +#endif // _WIN32 + +} // namespace + +// DiskInterface --------------------------------------------------------------- + +bool DiskInterface::MakeDirs(const string& path) { + string dir = DirName(path); + if (dir.empty()) + return true; // Reached root; assume it's there. + string err; + TimeStamp mtime = Stat(dir, &err); + if (mtime < 0) { + Error("%s", err.c_str()); + return false; + } + if (mtime > 0) + return true; // Exists already; we're done. + + // Directory doesn't exist. Try creating its parent first. + bool success = MakeDirs(dir); + if (!success) + return false; + return MakeDir(dir); +} + +// RealDiskInterface ----------------------------------------------------------- + +TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { + METRIC_RECORD("node stat"); +#ifdef _WIN32 + // MSDN: "Naming Files, Paths, and Namespaces" + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx + if (!path.empty() && path[0] != '\\' && path.size() > MAX_PATH) { + ostringstream err_stream; + err_stream << "Stat(" << path << "): Filename longer than " << MAX_PATH + << " characters"; + *err = err_stream.str(); + return -1; + } + if (!use_cache_) + return StatSingleFile(path, err); + + string dir = DirName(path); + string base(path.substr(dir.size() ? dir.size() + 1 : 0)); + + transform(dir.begin(), dir.end(), dir.begin(), ::tolower); + transform(base.begin(), base.end(), base.begin(), ::tolower); + + Cache::iterator ci = cache_.find(dir); + if (ci == cache_.end()) { + ci = cache_.insert(make_pair(dir, DirCache())).first; + if (!StatAllFilesInDir(dir.empty() ? "." : dir, &ci->second, err)) { + cache_.erase(ci); + return -1; + } + } + DirCache::iterator di = ci->second.find(base); + return di != ci->second.end() ? di->second : 0; +#else + struct stat st; + if (stat(path.c_str(), &st) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + return 0; + *err = "stat(" + path + "): " + strerror(errno); + return -1; + } + // Some users (Flatpak) set mtime to 0, this should be harmless + // and avoids conflicting with our return value of 0 meaning + // that it doesn't exist. + if (st.st_mtime == 0) + return 1; + return st.st_mtime; +#endif +} + +bool RealDiskInterface::WriteFile(const string& path, const string& contents) { + FILE* fp = fopen(path.c_str(), "w"); + if (fp == NULL) { + Error("WriteFile(%s): Unable to create file. %s", + path.c_str(), strerror(errno)); + return false; + } + + if (fwrite(contents.data(), 1, contents.length(), fp) < contents.length()) { + Error("WriteFile(%s): Unable to write to the file. %s", + path.c_str(), strerror(errno)); + fclose(fp); + return false; + } + + if (fclose(fp) == EOF) { + Error("WriteFile(%s): Unable to close the file. %s", + path.c_str(), strerror(errno)); + return false; + } + + return true; +} + +bool RealDiskInterface::MakeDir(const string& path) { + if (::MakeDir(path) < 0) { + if (errno == EEXIST) { + return true; + } + Error("mkdir(%s): %s", path.c_str(), strerror(errno)); + return false; + } + return true; +} + +FileReader::Status RealDiskInterface::ReadFile(const string& path, + string* contents, + string* err) { + switch (::ReadFile(path, contents, err)) { + case 0: return Okay; + case -ENOENT: return NotFound; + default: return OtherError; + } +} + +int RealDiskInterface::RemoveFile(const string& path) { + if (remove(path.c_str()) < 0) { + switch (errno) { + case ENOENT: + return 1; + default: + Error("remove(%s): %s", path.c_str(), strerror(errno)); + return -1; + } + } else { + return 0; + } +} + +void RealDiskInterface::AllowStatCache(bool allow) { +#ifdef _WIN32 + use_cache_ = allow; + if (!use_cache_) + cache_.clear(); +#endif +} diff --git a/src/3rdparty/ninja/src/disk_interface.h b/src/3rdparty/ninja/src/disk_interface.h new file mode 100644 index 00000000000..145e0892f49 --- /dev/null +++ b/src/3rdparty/ninja/src/disk_interface.h @@ -0,0 +1,100 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_DISK_INTERFACE_H_ +#define NINJA_DISK_INTERFACE_H_ + +#include +#include +using namespace std; + +#include "timestamp.h" + +/// Interface for reading files from disk. See DiskInterface for details. +/// This base offers the minimum interface needed just to read files. +struct FileReader { + virtual ~FileReader() {} + + /// Result of ReadFile. + enum Status { + Okay, + NotFound, + OtherError + }; + + /// Read and store in given string. On success, return Okay. + /// On error, return another Status and fill |err|. + virtual Status ReadFile(const string& path, string* contents, + string* err) = 0; +}; + +/// Interface for accessing the disk. +/// +/// Abstract so it can be mocked out for tests. The real implementation +/// is RealDiskInterface. +struct DiskInterface: public FileReader { + /// stat() a file, returning the mtime, or 0 if missing and -1 on + /// other errors. + virtual TimeStamp Stat(const string& path, string* err) const = 0; + + /// Create a directory, returning false on failure. + virtual bool MakeDir(const string& path) = 0; + + /// Create a file, with the specified name and contents + /// Returns true on success, false on failure + virtual bool WriteFile(const string& path, const string& contents) = 0; + + /// Remove the file named @a path. It behaves like 'rm -f path' so no errors + /// are reported if it does not exists. + /// @returns 0 if the file has been removed, + /// 1 if the file does not exist, and + /// -1 if an error occurs. + virtual int RemoveFile(const string& path) = 0; + + /// Create all the parent directories for path; like mkdir -p + /// `basename path`. + bool MakeDirs(const string& path); +}; + +/// Implementation of DiskInterface that actually hits the disk. +struct RealDiskInterface : public DiskInterface { + RealDiskInterface() +#ifdef _WIN32 + : use_cache_(false) +#endif + {} + virtual ~RealDiskInterface() {} + virtual TimeStamp Stat(const string& path, string* err) const; + virtual bool MakeDir(const string& path); + virtual bool WriteFile(const string& path, const string& contents); + virtual Status ReadFile(const string& path, string* contents, string* err); + virtual int RemoveFile(const string& path); + + /// Whether stat information can be cached. Only has an effect on Windows. + void AllowStatCache(bool allow); + + private: +#ifdef _WIN32 + /// Whether stat information can be cached. + bool use_cache_; + + typedef map DirCache; + // TODO: Neither a map nor a hashmap seems ideal here. If the statcache + // works out, come up with a better data structure. + typedef map Cache; + mutable Cache cache_; +#endif +}; + +#endif // NINJA_DISK_INTERFACE_H_ diff --git a/src/3rdparty/ninja/src/disk_interface_test.cc b/src/3rdparty/ninja/src/disk_interface_test.cc new file mode 100644 index 00000000000..d7fb8f8eb69 --- /dev/null +++ b/src/3rdparty/ninja/src/disk_interface_test.cc @@ -0,0 +1,311 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#ifdef _WIN32 +#include +#include +#endif + +#include "disk_interface.h" +#include "graph.h" +#include "test.h" + +namespace { + +struct DiskInterfaceTest : public testing::Test { + virtual void SetUp() { + // These tests do real disk accesses, so create a temp dir. + temp_dir_.CreateAndEnter("Ninja-DiskInterfaceTest"); + } + + virtual void TearDown() { + temp_dir_.Cleanup(); + } + + bool Touch(const char* path) { + FILE *f = fopen(path, "w"); + if (!f) + return false; + return fclose(f) == 0; + } + + ScopedTempDir temp_dir_; + RealDiskInterface disk_; +}; + +TEST_F(DiskInterfaceTest, StatMissingFile) { + string err; + EXPECT_EQ(0, disk_.Stat("nosuchfile", &err)); + EXPECT_EQ("", err); + + // On Windows, the errno for a file in a nonexistent directory + // is different. + EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err)); + EXPECT_EQ("", err); + + // On POSIX systems, the errno is different if a component of the + // path prefix is not a directory. + ASSERT_TRUE(Touch("notadir")); + EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err)); + EXPECT_EQ("", err); +} + +TEST_F(DiskInterfaceTest, StatBadPath) { + string err; +#ifdef _WIN32 + string bad_path("cc:\\foo"); + EXPECT_EQ(-1, disk_.Stat(bad_path, &err)); + EXPECT_NE("", err); +#else + string too_long_name(512, 'x'); + EXPECT_EQ(-1, disk_.Stat(too_long_name, &err)); + EXPECT_NE("", err); +#endif +} + +TEST_F(DiskInterfaceTest, StatExistingFile) { + string err; + ASSERT_TRUE(Touch("file")); + EXPECT_GT(disk_.Stat("file", &err), 1); + EXPECT_EQ("", err); +} + +TEST_F(DiskInterfaceTest, StatExistingDir) { + string err; + ASSERT_TRUE(disk_.MakeDir("subdir")); + ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir")); + EXPECT_GT(disk_.Stat(".", &err), 1); + EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("subdir", &err), 1); + EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1); + EXPECT_EQ("", err); + + EXPECT_EQ(disk_.Stat("subdir", &err), + disk_.Stat("subdir/.", &err)); + EXPECT_EQ(disk_.Stat("subdir", &err), + disk_.Stat("subdir/subsubdir/..", &err)); + EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err), + disk_.Stat("subdir/subsubdir/.", &err)); +} + +#ifdef _WIN32 +TEST_F(DiskInterfaceTest, StatCache) { + string err; + disk_.AllowStatCache(true); + + ASSERT_TRUE(Touch("file1")); + ASSERT_TRUE(Touch("fiLE2")); + ASSERT_TRUE(disk_.MakeDir("subdir")); + ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir")); + ASSERT_TRUE(Touch("subdir\\subfile1")); + ASSERT_TRUE(Touch("subdir\\SUBFILE2")); + ASSERT_TRUE(Touch("subdir\\SUBFILE3")); + + EXPECT_GT(disk_.Stat("FIle1", &err), 1); + EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("file1", &err), 1); + EXPECT_EQ("", err); + + EXPECT_GT(disk_.Stat("subdir/subfile2", &err), 1); + EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1); + EXPECT_EQ("", err); + + EXPECT_GT(disk_.Stat(".", &err), 1); + EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("subdir", &err), 1); + EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1); + EXPECT_EQ("", err); + + EXPECT_EQ(disk_.Stat("subdir", &err), + disk_.Stat("subdir/.", &err)); + EXPECT_EQ("", err); + EXPECT_EQ(disk_.Stat("subdir", &err), + disk_.Stat("subdir/subsubdir/..", &err)); + EXPECT_EQ("", err); + EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err), + disk_.Stat("subdir/subsubdir/.", &err)); + EXPECT_EQ("", err); + + // Test error cases. + string bad_path("cc:\\foo"); + EXPECT_EQ(-1, disk_.Stat(bad_path, &err)); + EXPECT_NE("", err); err.clear(); + EXPECT_EQ(-1, disk_.Stat(bad_path, &err)); + EXPECT_NE("", err); err.clear(); + EXPECT_EQ(0, disk_.Stat("nosuchfile", &err)); + EXPECT_EQ("", err); + EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err)); + EXPECT_EQ("", err); +} +#endif + +TEST_F(DiskInterfaceTest, ReadFile) { + string err; + std::string content; + ASSERT_EQ(DiskInterface::NotFound, + disk_.ReadFile("foobar", &content, &err)); + EXPECT_EQ("", content); + EXPECT_NE("", err); // actual value is platform-specific + err.clear(); + + const char* kTestFile = "testfile"; + FILE* f = fopen(kTestFile, "wb"); + ASSERT_TRUE(f); + const char* kTestContent = "test content\nok"; + fprintf(f, "%s", kTestContent); + ASSERT_EQ(0, fclose(f)); + + ASSERT_EQ(DiskInterface::Okay, + disk_.ReadFile(kTestFile, &content, &err)); + EXPECT_EQ(kTestContent, content); + EXPECT_EQ("", err); +} + +TEST_F(DiskInterfaceTest, MakeDirs) { + string path = "path/with/double//slash/"; + EXPECT_TRUE(disk_.MakeDirs(path.c_str())); + FILE* f = fopen((path + "a_file").c_str(), "w"); + EXPECT_TRUE(f); + EXPECT_EQ(0, fclose(f)); +#ifdef _WIN32 + string path2 = "another\\with\\back\\\\slashes\\"; + EXPECT_TRUE(disk_.MakeDirs(path2.c_str())); + FILE* f2 = fopen((path2 + "a_file").c_str(), "w"); + EXPECT_TRUE(f2); + EXPECT_EQ(0, fclose(f2)); +#endif +} + +TEST_F(DiskInterfaceTest, RemoveFile) { + const char* kFileName = "file-to-remove"; + ASSERT_TRUE(Touch(kFileName)); + EXPECT_EQ(0, disk_.RemoveFile(kFileName)); + EXPECT_EQ(1, disk_.RemoveFile(kFileName)); + EXPECT_EQ(1, disk_.RemoveFile("does not exist")); +} + +struct StatTest : public StateTestWithBuiltinRules, + public DiskInterface { + StatTest() : scan_(&state_, NULL, NULL, this) {} + + // DiskInterface implementation. + virtual TimeStamp Stat(const string& path, string* err) const; + virtual bool WriteFile(const string& path, const string& contents) { + assert(false); + return true; + } + virtual bool MakeDir(const string& path) { + assert(false); + return false; + } + virtual Status ReadFile(const string& path, string* contents, string* err) { + assert(false); + return NotFound; + } + virtual int RemoveFile(const string& path) { + assert(false); + return 0; + } + + DependencyScan scan_; + map mtimes_; + mutable vector stats_; +}; + +TimeStamp StatTest::Stat(const string& path, string* err) const { + stats_.push_back(path); + map::const_iterator i = mtimes_.find(path); + if (i == mtimes_.end()) + return 0; // File not found. + return i->second; +} + +TEST_F(StatTest, Simple) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat in\n")); + + Node* out = GetNode("out"); + string err; + EXPECT_TRUE(out->Stat(this, &err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, stats_.size()); + scan_.RecomputeDirty(out, NULL); + ASSERT_EQ(2u, stats_.size()); + ASSERT_EQ("out", stats_[0]); + ASSERT_EQ("in", stats_[1]); +} + +TEST_F(StatTest, TwoStep) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat mid\n" +"build mid: cat in\n")); + + Node* out = GetNode("out"); + string err; + EXPECT_TRUE(out->Stat(this, &err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, stats_.size()); + scan_.RecomputeDirty(out, NULL); + ASSERT_EQ(3u, stats_.size()); + ASSERT_EQ("out", stats_[0]); + ASSERT_TRUE(GetNode("out")->dirty()); + ASSERT_EQ("mid", stats_[1]); + ASSERT_TRUE(GetNode("mid")->dirty()); + ASSERT_EQ("in", stats_[2]); +} + +TEST_F(StatTest, Tree) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat mid1 mid2\n" +"build mid1: cat in11 in12\n" +"build mid2: cat in21 in22\n")); + + Node* out = GetNode("out"); + string err; + EXPECT_TRUE(out->Stat(this, &err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, stats_.size()); + scan_.RecomputeDirty(out, NULL); + ASSERT_EQ(1u + 6u, stats_.size()); + ASSERT_EQ("mid1", stats_[1]); + ASSERT_TRUE(GetNode("mid1")->dirty()); + ASSERT_EQ("in11", stats_[2]); +} + +TEST_F(StatTest, Middle) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat mid\n" +"build mid: cat in\n")); + + mtimes_["in"] = 1; + mtimes_["mid"] = 0; // missing + mtimes_["out"] = 1; + + Node* out = GetNode("out"); + string err; + EXPECT_TRUE(out->Stat(this, &err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, stats_.size()); + scan_.RecomputeDirty(out, NULL); + ASSERT_FALSE(GetNode("in")->dirty()); + ASSERT_TRUE(GetNode("mid")->dirty()); + ASSERT_TRUE(GetNode("out")->dirty()); +} + +} // namespace diff --git a/src/3rdparty/ninja/src/edit_distance.cc b/src/3rdparty/ninja/src/edit_distance.cc new file mode 100644 index 00000000000..3bb62b85899 --- /dev/null +++ b/src/3rdparty/ninja/src/edit_distance.cc @@ -0,0 +1,69 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "edit_distance.h" + +#include +#include + +int EditDistance(const StringPiece& s1, + const StringPiece& s2, + bool allow_replacements, + int max_edit_distance) { + // The algorithm implemented below is the "classic" + // dynamic-programming algorithm for computing the Levenshtein + // distance, which is described here: + // + // http://en.wikipedia.org/wiki/Levenshtein_distance + // + // Although the algorithm is typically described using an m x n + // array, only one row plus one element are used at a time, so this + // implementation just keeps one vector for the row. To update one entry, + // only the entries to the left, top, and top-left are needed. The left + // entry is in row[x-1], the top entry is what's in row[x] from the last + // iteration, and the top-left entry is stored in previous. + int m = s1.len_; + int n = s2.len_; + + vector row(n + 1); + for (int i = 1; i <= n; ++i) + row[i] = i; + + for (int y = 1; y <= m; ++y) { + row[0] = y; + int best_this_row = row[0]; + + int previous = y - 1; + for (int x = 1; x <= n; ++x) { + int old_row = row[x]; + if (allow_replacements) { + row[x] = min(previous + (s1.str_[y - 1] == s2.str_[x - 1] ? 0 : 1), + min(row[x - 1], row[x]) + 1); + } + else { + if (s1.str_[y - 1] == s2.str_[x - 1]) + row[x] = previous; + else + row[x] = min(row[x - 1], row[x]) + 1; + } + previous = old_row; + best_this_row = min(best_this_row, row[x]); + } + + if (max_edit_distance && best_this_row > max_edit_distance) + return max_edit_distance + 1; + } + + return row[n]; +} diff --git a/src/3rdparty/ninja/src/edit_distance.h b/src/3rdparty/ninja/src/edit_distance.h new file mode 100644 index 00000000000..45ae4aecd31 --- /dev/null +++ b/src/3rdparty/ninja/src/edit_distance.h @@ -0,0 +1,25 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_EDIT_DISTANCE_H_ +#define NINJA_EDIT_DISTANCE_H_ + +#include "string_piece.h" + +int EditDistance(const StringPiece& s1, + const StringPiece& s2, + bool allow_replacements = true, + int max_edit_distance = 0); + +#endif // NINJA_EDIT_DISTANCE_H_ diff --git a/src/3rdparty/ninja/src/edit_distance_test.cc b/src/3rdparty/ninja/src/edit_distance_test.cc new file mode 100644 index 00000000000..9dc0f827a75 --- /dev/null +++ b/src/3rdparty/ninja/src/edit_distance_test.cc @@ -0,0 +1,48 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "edit_distance.h" + +#include "test.h" + +TEST(EditDistanceTest, TestEmpty) { + EXPECT_EQ(5, EditDistance("", "ninja")); + EXPECT_EQ(5, EditDistance("ninja", "")); + EXPECT_EQ(0, EditDistance("", "")); +} + +TEST(EditDistanceTest, TestMaxDistance) { + const bool allow_replacements = true; + for (int max_distance = 1; max_distance < 7; ++max_distance) { + EXPECT_EQ(max_distance + 1, + EditDistance("abcdefghijklmnop", "ponmlkjihgfedcba", + allow_replacements, max_distance)); + } +} + +TEST(EditDistanceTest, TestAllowReplacements) { + bool allow_replacements = true; + EXPECT_EQ(1, EditDistance("ninja", "njnja", allow_replacements)); + EXPECT_EQ(1, EditDistance("njnja", "ninja", allow_replacements)); + + allow_replacements = false; + EXPECT_EQ(2, EditDistance("ninja", "njnja", allow_replacements)); + EXPECT_EQ(2, EditDistance("njnja", "ninja", allow_replacements)); +} + +TEST(EditDistanceTest, TestBasics) { + EXPECT_EQ(0, EditDistance("browser_tests", "browser_tests")); + EXPECT_EQ(1, EditDistance("browser_test", "browser_tests")); + EXPECT_EQ(1, EditDistance("browser_tests", "browser_test")); +} diff --git a/src/3rdparty/ninja/src/eval_env.cc b/src/3rdparty/ninja/src/eval_env.cc new file mode 100644 index 00000000000..8817a8732c7 --- /dev/null +++ b/src/3rdparty/ninja/src/eval_env.cc @@ -0,0 +1,132 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "eval_env.h" + +string BindingEnv::LookupVariable(const string& var) { + map::iterator i = bindings_.find(var); + if (i != bindings_.end()) + return i->second; + if (parent_) + return parent_->LookupVariable(var); + return ""; +} + +void BindingEnv::AddBinding(const string& key, const string& val) { + bindings_[key] = val; +} + +void BindingEnv::AddRule(const Rule* rule) { + assert(LookupRuleCurrentScope(rule->name()) == NULL); + rules_[rule->name()] = rule; +} + +const Rule* BindingEnv::LookupRuleCurrentScope(const string& rule_name) { + map::iterator i = rules_.find(rule_name); + if (i == rules_.end()) + return NULL; + return i->second; +} + +const Rule* BindingEnv::LookupRule(const string& rule_name) { + map::iterator i = rules_.find(rule_name); + if (i != rules_.end()) + return i->second; + if (parent_) + return parent_->LookupRule(rule_name); + return NULL; +} + +void Rule::AddBinding(const string& key, const EvalString& val) { + bindings_[key] = val; +} + +const EvalString* Rule::GetBinding(const string& key) const { + Bindings::const_iterator i = bindings_.find(key); + if (i == bindings_.end()) + return NULL; + return &i->second; +} + +// static +bool Rule::IsReservedBinding(const string& var) { + return var == "command" || + var == "depfile" || + var == "description" || + var == "deps" || + var == "generator" || + var == "pool" || + var == "restat" || + var == "rspfile" || + var == "rspfile_content" || + var == "msvc_deps_prefix"; +} + +const map& BindingEnv::GetRules() const { + return rules_; +} + +string BindingEnv::LookupWithFallback(const string& var, + const EvalString* eval, + Env* env) { + map::iterator i = bindings_.find(var); + if (i != bindings_.end()) + return i->second; + + if (eval) + return eval->Evaluate(env); + + if (parent_) + return parent_->LookupVariable(var); + + return ""; +} + +string EvalString::Evaluate(Env* env) const { + string result; + for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) { + if (i->second == RAW) + result.append(i->first); + else + result.append(env->LookupVariable(i->first)); + } + return result; +} + +void EvalString::AddText(StringPiece text) { + // Add it to the end of an existing RAW token if possible. + if (!parsed_.empty() && parsed_.back().second == RAW) { + parsed_.back().first.append(text.str_, text.len_); + } else { + parsed_.push_back(make_pair(text.AsString(), RAW)); + } +} +void EvalString::AddSpecial(StringPiece text) { + parsed_.push_back(make_pair(text.AsString(), SPECIAL)); +} + +string EvalString::Serialize() const { + string result; + for (TokenList::const_iterator i = parsed_.begin(); + i != parsed_.end(); ++i) { + result.append("["); + if (i->second == SPECIAL) + result.append("$"); + result.append(i->first); + result.append("]"); + } + return result; +} diff --git a/src/3rdparty/ninja/src/eval_env.h b/src/3rdparty/ninja/src/eval_env.h new file mode 100644 index 00000000000..999ce42d141 --- /dev/null +++ b/src/3rdparty/ninja/src/eval_env.h @@ -0,0 +1,105 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_EVAL_ENV_H_ +#define NINJA_EVAL_ENV_H_ + +#include +#include +#include +using namespace std; + +#include "string_piece.h" + +struct Rule; + +/// An interface for a scope for variable (e.g. "$foo") lookups. +struct Env { + virtual ~Env() {} + virtual string LookupVariable(const string& var) = 0; +}; + +/// A tokenized string that contains variable references. +/// Can be evaluated relative to an Env. +struct EvalString { + string Evaluate(Env* env) const; + + void Clear() { parsed_.clear(); } + bool empty() const { return parsed_.empty(); } + + void AddText(StringPiece text); + void AddSpecial(StringPiece text); + + /// Construct a human-readable representation of the parsed state, + /// for use in tests. + string Serialize() const; + +private: + enum TokenType { RAW, SPECIAL }; + typedef vector > TokenList; + TokenList parsed_; +}; + +/// An invokable build command and associated metadata (description, etc.). +struct Rule { + explicit Rule(const string& name) : name_(name) {} + + const string& name() const { return name_; } + + void AddBinding(const string& key, const EvalString& val); + + static bool IsReservedBinding(const string& var); + + const EvalString* GetBinding(const string& key) const; + + private: + // Allow the parsers to reach into this object and fill out its fields. + friend struct ManifestParser; + + string name_; + typedef map Bindings; + Bindings bindings_; +}; + +/// An Env which contains a mapping of variables to values +/// as well as a pointer to a parent scope. +struct BindingEnv : public Env { + BindingEnv() : parent_(NULL) {} + explicit BindingEnv(BindingEnv* parent) : parent_(parent) {} + + virtual ~BindingEnv() {} + virtual string LookupVariable(const string& var); + + void AddRule(const Rule* rule); + const Rule* LookupRule(const string& rule_name); + const Rule* LookupRuleCurrentScope(const string& rule_name); + const map& GetRules() const; + + void AddBinding(const string& key, const string& val); + + /// This is tricky. Edges want lookup scope to go in this order: + /// 1) value set on edge itself (edge_->env_) + /// 2) value set on rule, with expansion in the edge's scope + /// 3) value set on enclosing scope of edge (edge_->env_->parent_) + /// This function takes as parameters the necessary info to do (2). + string LookupWithFallback(const string& var, const EvalString* eval, + Env* env); + +private: + map bindings_; + map rules_; + BindingEnv* parent_; +}; + +#endif // NINJA_EVAL_ENV_H_ diff --git a/src/3rdparty/ninja/src/exit_status.h b/src/3rdparty/ninja/src/exit_status.h new file mode 100644 index 00000000000..a714ece791f --- /dev/null +++ b/src/3rdparty/ninja/src/exit_status.h @@ -0,0 +1,24 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_EXIT_STATUS_H_ +#define NINJA_EXIT_STATUS_H_ + +enum ExitStatus { + ExitSuccess, + ExitFailure, + ExitInterrupted +}; + +#endif // NINJA_EXIT_STATUS_H_ diff --git a/src/3rdparty/ninja/src/gen_doxygen_mainpage.sh b/src/3rdparty/ninja/src/gen_doxygen_mainpage.sh new file mode 100755 index 00000000000..d1599477ebd --- /dev/null +++ b/src/3rdparty/ninja/src/gen_doxygen_mainpage.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o nounset + +STATUS=0 + +# Print each of its arguments on stderr (one per line) prefixed by the +# basename of this script. +stderr() +{ + local me=$(basename "$0") + local i + for i + do + echo >&2 "$me: $i" + done +} + +# Print each of its arguments on stderr (one per line) prefixed by the +# basename of this script and 'error'. +error() +{ + local i + for i + do + stderr "error: $i" + done + STATUS=1 +} + +generate_header() +{ + cat <&2 "usage: $0 inputs..." + exit 1 +fi + +generate_header +for i in "$@" +do + include_file "$i" +done +generate_footer + +exit $STATUS diff --git a/src/3rdparty/ninja/src/getopt.c b/src/3rdparty/ninja/src/getopt.c new file mode 100644 index 00000000000..0c2ef356e38 --- /dev/null +++ b/src/3rdparty/ninja/src/getopt.c @@ -0,0 +1,410 @@ +/**************************************************************************** + +getopt.c - Read command line options + +AUTHOR: Gregory Pietsch +CREATED Fri Jan 10 21:13:05 1997 + +DESCRIPTION: + +The getopt() function parses the command line arguments. Its arguments argc +and argv are the argument count and array as passed to the main() function +on program invocation. The argument optstring is a list of available option +characters. If such a character is followed by a colon (`:'), the option +takes an argument, which is placed in optarg. If such a character is +followed by two colons, the option takes an optional argument, which is +placed in optarg. If the option does not take an argument, optarg is NULL. + +The external variable optind is the index of the next array element of argv +to be processed; it communicates from one call to the next which element to +process. + +The getopt_long() function works like getopt() except that it also accepts +long options started by two dashes `--'. If these take values, it is either +in the form + +--arg=value + + or + +--arg value + +It takes the additional arguments longopts which is a pointer to the first +element of an array of type GETOPT_LONG_OPTION_T. The last element of the +array has to be filled with NULL for the name field. + +The longind pointer points to the index of the current long option relative +to longopts if it is non-NULL. + +The getopt() function returns the option character if the option was found +successfully, `:' if there was a missing parameter for one of the options, +`?' for an unknown option character, and EOF for the end of the option list. + +The getopt_long() function's return value is described in the header file. + +The function getopt_long_only() is identical to getopt_long(), except that a +plus sign `+' can introduce long options as well as `--'. + +The following describes how to deal with options that follow non-option +argv-elements. + +If the caller did not specify anything, the default is REQUIRE_ORDER if the +environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. + +REQUIRE_ORDER means don't recognize them as options; stop option processing +when the first non-option is seen. This is what Unix does. This mode of +operation is selected by either setting the environment variable +POSIXLY_CORRECT, or using `+' as the first character of the optstring +parameter. + +PERMUTE is the default. We permute the contents of ARGV as we scan, so that +eventually all the non-options are at the end. This allows options to be +given in any order, even with programs that were not written to expect this. + +RETURN_IN_ORDER is an option available to programs that were written to +expect options and other argv-elements in any order and that care about the +ordering of the two. We describe each non-option argv-element as if it were +the argument of an option with character code 1. Using `-' as the first +character of the optstring parameter selects this mode of operation. + +The special argument `--' forces an end of option-scanning regardless of the +value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause +getopt() and friends to return EOF with optind != argc. + +COPYRIGHT NOTICE AND DISCLAIMER: + +Copyright (C) 1997 Gregory Pietsch + +This file and the accompanying getopt.h header file are hereby placed in the +public domain without restrictions. Just give the author credit, don't +claim you wrote it or prevent anyone else from using it. + +Gregory Pietsch's current e-mail address: +gpietsch@comcast.net +****************************************************************************/ + +/* include files */ +#include +#include +#include +#ifndef GETOPT_H +#include "getopt.h" +#endif + +/* macros */ + +/* types */ +typedef enum GETOPT_ORDERING_T +{ + PERMUTE, + RETURN_IN_ORDER, + REQUIRE_ORDER +} GETOPT_ORDERING_T; + +/* globally-defined variables */ +char *optarg = NULL; +int optind = 0; +int opterr = 1; +int optopt = '?'; + +/* functions */ + +/* reverse_argv_elements: reverses num elements starting at argv */ +static void +reverse_argv_elements (char **argv, int num) +{ + int i; + char *tmp; + + for (i = 0; i < (num >> 1); i++) + { + tmp = argv[i]; + argv[i] = argv[num - i - 1]; + argv[num - i - 1] = tmp; + } +} + +/* permute: swap two blocks of argv-elements given their lengths */ +static void +permute (char **argv, int len1, int len2) +{ + reverse_argv_elements (argv, len1); + reverse_argv_elements (argv, len1 + len2); + reverse_argv_elements (argv, len2); +} + +/* is_option: is this argv-element an option or the end of the option list? */ +static int +is_option (char *argv_element, int only) +{ + return ((argv_element == NULL) + || (argv_element[0] == '-') || (only && argv_element[0] == '+')); +} + +/* getopt_internal: the function that does all the dirty work */ +static int +getopt_internal (int argc, char **argv, char *shortopts, + GETOPT_LONG_OPTION_T * longopts, int *longind, int only) +{ + GETOPT_ORDERING_T ordering = PERMUTE; + static size_t optwhere = 0; + size_t permute_from = 0; + int num_nonopts = 0; + int optindex = 0; + size_t match_chars = 0; + char *possible_arg = NULL; + int longopt_match = -1; + int has_arg = -1; + char *cp = NULL; + int arg_next = 0; + + /* first, deal with silly parameters and easy stuff */ + if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL)) + return (optopt = '?'); + if (optind >= argc || argv[optind] == NULL) + return EOF; + if (strcmp (argv[optind], "--") == 0) + { + optind++; + return EOF; + } + /* if this is our first time through */ + if (optind == 0) + optind = optwhere = 1; + + /* define ordering */ + if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+')) + { + ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER; + shortopts++; + } + else + ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE; + + /* + * based on ordering, find our next option, if we're at the beginning of + * one + */ + if (optwhere == 1) + { + switch (ordering) + { + case PERMUTE: + permute_from = optind; + num_nonopts = 0; + while (!is_option (argv[optind], only)) + { + optind++; + num_nonopts++; + } + if (argv[optind] == NULL) + { + /* no more options */ + optind = permute_from; + return EOF; + } + else if (strcmp (argv[optind], "--") == 0) + { + /* no more options, but have to get `--' out of the way */ + permute (argv + permute_from, num_nonopts, 1); + optind = permute_from + 1; + return EOF; + } + break; + case RETURN_IN_ORDER: + if (!is_option (argv[optind], only)) + { + optarg = argv[optind++]; + return (optopt = 1); + } + break; + case REQUIRE_ORDER: + if (!is_option (argv[optind], only)) + return EOF; + break; + } + } + /* we've got an option, so parse it */ + + /* first, is it a long option? */ + if (longopts != NULL + && (memcmp (argv[optind], "--", 2) == 0 + || (only && argv[optind][0] == '+')) && optwhere == 1) + { + /* handle long options */ + if (memcmp (argv[optind], "--", 2) == 0) + optwhere = 2; + longopt_match = -1; + possible_arg = strchr (argv[optind] + optwhere, '='); + if (possible_arg == NULL) + { + /* no =, so next argv might be arg */ + match_chars = strlen (argv[optind]); + possible_arg = argv[optind] + match_chars; + match_chars = match_chars - optwhere; + } + else + match_chars = (possible_arg - argv[optind]) - optwhere; + for (optindex = 0; longopts[optindex].name != NULL; optindex++) + { + if (memcmp (argv[optind] + optwhere, + longopts[optindex].name, match_chars) == 0) + { + /* do we have an exact match? */ + if (match_chars == strlen (longopts[optindex].name)) + { + longopt_match = optindex; + break; + } + /* do any characters match? */ + else + { + if (longopt_match < 0) + longopt_match = optindex; + else + { + /* we have ambiguous options */ + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous " + "(could be `--%s' or `--%s')\n", + argv[0], + argv[optind], + longopts[longopt_match].name, + longopts[optindex].name); + return (optopt = '?'); + } + } + } + } + if (longopt_match >= 0) + has_arg = longopts[longopt_match].has_arg; + } + /* if we didn't find a long option, is it a short option? */ + if (longopt_match < 0 && shortopts != NULL) + { + cp = strchr (shortopts, argv[optind][optwhere]); + if (cp == NULL) + { + /* couldn't find option in shortopts */ + if (opterr) + fprintf (stderr, + "%s: invalid option -- `-%c'\n", + argv[0], argv[optind][optwhere]); + optwhere++; + if (argv[optind][optwhere] == '\0') + { + optind++; + optwhere = 1; + } + return (optopt = '?'); + } + has_arg = ((cp[1] == ':') + ? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument); + possible_arg = argv[optind] + optwhere + 1; + optopt = *cp; + } + /* get argument and reset optwhere */ + arg_next = 0; + switch (has_arg) + { + case OPTIONAL_ARG: + if (*possible_arg == '=') + possible_arg++; + if (*possible_arg != '\0') + { + optarg = possible_arg; + optwhere = 1; + } + else + optarg = NULL; + break; + case required_argument: + if (*possible_arg == '=') + possible_arg++; + if (*possible_arg != '\0') + { + optarg = possible_arg; + optwhere = 1; + } + else if (optind + 1 >= argc) + { + if (opterr) + { + fprintf (stderr, "%s: argument required for option `", argv[0]); + if (longopt_match >= 0) + fprintf (stderr, "--%s'\n", longopts[longopt_match].name); + else + fprintf (stderr, "-%c'\n", *cp); + } + optind++; + return (optopt = ':'); + } + else + { + optarg = argv[optind + 1]; + arg_next = 1; + optwhere = 1; + } + break; + case no_argument: + if (longopt_match < 0) + { + optwhere++; + if (argv[optind][optwhere] == '\0') + optwhere = 1; + } + else + optwhere = 1; + optarg = NULL; + break; + } + + /* do we have to permute or otherwise modify optind? */ + if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0) + { + permute (argv + permute_from, num_nonopts, 1 + arg_next); + optind = permute_from + 1 + arg_next; + } + else if (optwhere == 1) + optind = optind + 1 + arg_next; + + /* finally return */ + if (longopt_match >= 0) + { + if (longind != NULL) + *longind = longopt_match; + if (longopts[longopt_match].flag != NULL) + { + *(longopts[longopt_match].flag) = longopts[longopt_match].val; + return 0; + } + else + return longopts[longopt_match].val; + } + else + return optopt; +} + +#ifndef _AIX +int +getopt (int argc, char **argv, char *optstring) +{ + return getopt_internal (argc, argv, optstring, NULL, NULL, 0); +} +#endif + +int +getopt_long (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind) +{ + return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0); +} + +int +getopt_long_only (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind) +{ + return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1); +} + +/* end of file GETOPT.C */ diff --git a/src/3rdparty/ninja/src/getopt.h b/src/3rdparty/ninja/src/getopt.h new file mode 100644 index 00000000000..965dc29003c --- /dev/null +++ b/src/3rdparty/ninja/src/getopt.h @@ -0,0 +1,57 @@ +#ifndef GETOPT_H +#define GETOPT_H + +/* include files needed by this include file */ + +/* macros defined by this include file */ +#define no_argument 0 +#define required_argument 1 +#define OPTIONAL_ARG 2 + +/* types defined by this include file */ + +/* GETOPT_LONG_OPTION_T: The type of long option */ +typedef struct GETOPT_LONG_OPTION_T +{ + const char *name; /* the name of the long option */ + int has_arg; /* one of the above macros */ + int *flag; /* determines if getopt_long() returns a + * value for a long option; if it is + * non-NULL, 0 is returned as a function + * value and the value of val is stored in + * the area pointed to by flag. Otherwise, + * val is returned. */ + int val; /* determines the value to return if flag is + * NULL. */ +} GETOPT_LONG_OPTION_T; + +typedef GETOPT_LONG_OPTION_T option; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* externally-defined variables */ + extern char *optarg; + extern int optind; + extern int opterr; + extern int optopt; + + /* function prototypes */ +#ifndef _AIX + int getopt (int argc, char **argv, char *optstring); +#endif + int getopt_long (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind); + int getopt_long_only (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind); + +#ifdef __cplusplus +}; + +#endif + +#endif /* GETOPT_H */ + +/* END OF FILE getopt.h */ diff --git a/src/3rdparty/ninja/src/graph.cc b/src/3rdparty/ninja/src/graph.cc new file mode 100644 index 00000000000..ce4ea774f24 --- /dev/null +++ b/src/3rdparty/ninja/src/graph.cc @@ -0,0 +1,589 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "graph.h" + +#include +#include + +#include "build_log.h" +#include "debug_flags.h" +#include "depfile_parser.h" +#include "deps_log.h" +#include "disk_interface.h" +#include "manifest_parser.h" +#include "metrics.h" +#include "state.h" +#include "util.h" + +bool Node::Stat(DiskInterface* disk_interface, string* err) { + return (mtime_ = disk_interface->Stat(path_, err)) != -1; +} + +bool DependencyScan::RecomputeDirty(Node* node, string* err) { + vector stack; + return RecomputeDirty(node, &stack, err); +} + +bool DependencyScan::RecomputeDirty(Node* node, vector* stack, + string* err) { + Edge* edge = node->in_edge(); + if (!edge) { + // If we already visited this leaf node then we are done. + if (node->status_known()) + return true; + // This node has no in-edge; it is dirty if it is missing. + if (!node->StatIfNecessary(disk_interface_, err)) + return false; + if (!node->exists()) + EXPLAIN("%s has no in-edge and is missing", node->path().c_str()); + node->set_dirty(!node->exists()); + return true; + } + + // If we already finished this edge then we are done. + if (edge->mark_ == Edge::VisitDone) + return true; + + // If we encountered this edge earlier in the call stack we have a cycle. + if (!VerifyDAG(node, stack, err)) + return false; + + // Mark the edge temporarily while in the call stack. + edge->mark_ = Edge::VisitInStack; + stack->push_back(node); + + bool dirty = false; + edge->outputs_ready_ = true; + edge->deps_missing_ = false; + + // Load output mtimes so we can compare them to the most recent input below. + for (vector::iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) { + if (!(*o)->StatIfNecessary(disk_interface_, err)) + return false; + } + + if (!dep_loader_.LoadDeps(edge, err)) { + if (!err->empty()) + return false; + // Failed to load dependency info: rebuild to regenerate it. + // LoadDeps() did EXPLAIN() already, no need to do it here. + dirty = edge->deps_missing_ = true; + } + + // Visit all inputs; we're dirty if any of the inputs are dirty. + Node* most_recent_input = NULL; + for (vector::iterator i = edge->inputs_.begin(); + i != edge->inputs_.end(); ++i) { + // Visit this input. + if (!RecomputeDirty(*i, stack, err)) + return false; + + // If an input is not ready, neither are our outputs. + if (Edge* in_edge = (*i)->in_edge()) { + if (!in_edge->outputs_ready_) + edge->outputs_ready_ = false; + } + + if (!edge->is_order_only(i - edge->inputs_.begin())) { + // If a regular input is dirty (or missing), we're dirty. + // Otherwise consider mtime. + if ((*i)->dirty()) { + EXPLAIN("%s is dirty", (*i)->path().c_str()); + dirty = true; + } else { + if (!most_recent_input || (*i)->mtime() > most_recent_input->mtime()) { + most_recent_input = *i; + } + } + } + } + + // We may also be dirty due to output state: missing outputs, out of + // date outputs, etc. Visit all outputs and determine whether they're dirty. + if (!dirty) + if (!RecomputeOutputsDirty(edge, most_recent_input, &dirty, err)) + return false; + + // Finally, visit each output and update their dirty state if necessary. + for (vector::iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) { + if (dirty) + (*o)->MarkDirty(); + } + + // If an edge is dirty, its outputs are normally not ready. (It's + // possible to be clean but still not be ready in the presence of + // order-only inputs.) + // But phony edges with no inputs have nothing to do, so are always + // ready. + if (dirty && !(edge->is_phony() && edge->inputs_.empty())) + edge->outputs_ready_ = false; + + // Mark the edge as finished during this walk now that it will no longer + // be in the call stack. + edge->mark_ = Edge::VisitDone; + assert(stack->back() == node); + stack->pop_back(); + + return true; +} + +bool DependencyScan::VerifyDAG(Node* node, vector* stack, string* err) { + Edge* edge = node->in_edge(); + assert(edge != NULL); + + // If we have no temporary mark on the edge then we do not yet have a cycle. + if (edge->mark_ != Edge::VisitInStack) + return true; + + // We have this edge earlier in the call stack. Find it. + vector::iterator start = stack->begin(); + while (start != stack->end() && (*start)->in_edge() != edge) + ++start; + assert(start != stack->end()); + + // Make the cycle clear by reporting its start as the node at its end + // instead of some other output of the starting edge. For example, + // running 'ninja b' on + // build a b: cat c + // build c: cat a + // should report a -> c -> a instead of b -> c -> a. + *start = node; + + // Construct the error message rejecting the cycle. + *err = "dependency cycle: "; + for (vector::const_iterator i = start; i != stack->end(); ++i) { + err->append((*i)->path()); + err->append(" -> "); + } + err->append((*start)->path()); + + if ((start + 1) == stack->end() && edge->maybe_phonycycle_diagnostic()) { + // The manifest parser would have filtered out the self-referencing + // input if it were not configured to allow the error. + err->append(" [-w phonycycle=err]"); + } + + return false; +} + +bool DependencyScan::RecomputeOutputsDirty(Edge* edge, Node* most_recent_input, + bool* outputs_dirty, string* err) { + string command = edge->EvaluateCommand(/*incl_rsp_file=*/true); + for (vector::iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) { + if (RecomputeOutputDirty(edge, most_recent_input, command, *o)) { + *outputs_dirty = true; + return true; + } + } + return true; +} + +bool DependencyScan::RecomputeOutputDirty(Edge* edge, + Node* most_recent_input, + const string& command, + Node* output) { + if (edge->is_phony()) { + // Phony edges don't write any output. Outputs are only dirty if + // there are no inputs and we're missing the output. + if (edge->inputs_.empty() && !output->exists()) { + EXPLAIN("output %s of phony edge with no inputs doesn't exist", + output->path().c_str()); + return true; + } + return false; + } + + BuildLog::LogEntry* entry = 0; + + // Dirty if we're missing the output. + if (!output->exists()) { + EXPLAIN("output %s doesn't exist", output->path().c_str()); + return true; + } + + // Dirty if the output is older than the input. + if (most_recent_input && output->mtime() < most_recent_input->mtime()) { + TimeStamp output_mtime = output->mtime(); + + // If this is a restat rule, we may have cleaned the output with a restat + // rule in a previous run and stored the most recent input mtime in the + // build log. Use that mtime instead, so that the file will only be + // considered dirty if an input was modified since the previous run. + bool used_restat = false; + if (edge->GetBindingBool("restat") && build_log() && + (entry = build_log()->LookupByOutput(output->path()))) { + output_mtime = entry->mtime; + used_restat = true; + } + + if (output_mtime < most_recent_input->mtime()) { + EXPLAIN("%soutput %s older than most recent input %s " + "(%d vs %d)", + used_restat ? "restat of " : "", output->path().c_str(), + most_recent_input->path().c_str(), + output_mtime, most_recent_input->mtime()); + return true; + } + } + + if (build_log()) { + bool generator = edge->GetBindingBool("generator"); + if (entry || (entry = build_log()->LookupByOutput(output->path()))) { + if (!generator && + BuildLog::LogEntry::HashCommand(command) != entry->command_hash) { + // May also be dirty due to the command changing since the last build. + // But if this is a generator rule, the command changing does not make us + // dirty. + EXPLAIN("command line changed for %s", output->path().c_str()); + return true; + } + if (most_recent_input && entry->mtime < most_recent_input->mtime()) { + // May also be dirty due to the mtime in the log being older than the + // mtime of the most recent input. This can occur even when the mtime + // on disk is newer if a previous run wrote to the output file but + // exited with an error or was interrupted. + EXPLAIN("recorded mtime of %s older than most recent input %s (%d vs %d)", + output->path().c_str(), most_recent_input->path().c_str(), + entry->mtime, most_recent_input->mtime()); + return true; + } + } + if (!entry && !generator) { + EXPLAIN("command line not found in log for %s", output->path().c_str()); + return true; + } + } + + return false; +} + +bool Edge::AllInputsReady() const { + for (vector::const_iterator i = inputs_.begin(); + i != inputs_.end(); ++i) { + if ((*i)->in_edge() && !(*i)->in_edge()->outputs_ready()) + return false; + } + return true; +} + +/// An Env for an Edge, providing $in and $out. +struct EdgeEnv : public Env { + enum EscapeKind { kShellEscape, kDoNotEscape }; + + EdgeEnv(Edge* edge, EscapeKind escape) + : edge_(edge), escape_in_out_(escape), recursive_(false) {} + virtual string LookupVariable(const string& var); + + /// Given a span of Nodes, construct a list of paths suitable for a command + /// line. + string MakePathList(vector::iterator begin, + vector::iterator end, + char sep); + + private: + vector lookups_; + Edge* edge_; + EscapeKind escape_in_out_; + bool recursive_; +}; + +string EdgeEnv::LookupVariable(const string& var) { + if (var == "in" || var == "in_newline") { + int explicit_deps_count = edge_->inputs_.size() - edge_->implicit_deps_ - + edge_->order_only_deps_; + return MakePathList(edge_->inputs_.begin(), + edge_->inputs_.begin() + explicit_deps_count, + var == "in" ? ' ' : '\n'); + } else if (var == "out") { + int explicit_outs_count = edge_->outputs_.size() - edge_->implicit_outs_; + return MakePathList(edge_->outputs_.begin(), + edge_->outputs_.begin() + explicit_outs_count, + ' '); + } + + if (recursive_) { + vector::const_iterator it; + if ((it = find(lookups_.begin(), lookups_.end(), var)) != lookups_.end()) { + string cycle; + for (; it != lookups_.end(); ++it) + cycle.append(*it + " -> "); + cycle.append(var); + Fatal(("cycle in rule variables: " + cycle).c_str()); + } + } + + // See notes on BindingEnv::LookupWithFallback. + const EvalString* eval = edge_->rule_->GetBinding(var); + if (recursive_ && eval) + lookups_.push_back(var); + + // In practice, variables defined on rules never use another rule variable. + // For performance, only start checking for cycles after the first lookup. + recursive_ = true; + return edge_->env_->LookupWithFallback(var, eval, this); +} + +string EdgeEnv::MakePathList(vector::iterator begin, + vector::iterator end, + char sep) { + string result; + for (vector::iterator i = begin; i != end; ++i) { + if (!result.empty()) + result.push_back(sep); + const string& path = (*i)->PathDecanonicalized(); + if (escape_in_out_ == kShellEscape) { +#if _WIN32 + GetWin32EscapedString(path, &result); +#else + GetShellEscapedString(path, &result); +#endif + } else { + result.append(path); + } + } + return result; +} + +string Edge::EvaluateCommand(bool incl_rsp_file) { + string command = GetBinding("command"); + if (incl_rsp_file) { + string rspfile_content = GetBinding("rspfile_content"); + if (!rspfile_content.empty()) + command += ";rspfile=" + rspfile_content; + } + return command; +} + +string Edge::GetBinding(const string& key) { + EdgeEnv env(this, EdgeEnv::kShellEscape); + return env.LookupVariable(key); +} + +bool Edge::GetBindingBool(const string& key) { + return !GetBinding(key).empty(); +} + +string Edge::GetUnescapedDepfile() { + EdgeEnv env(this, EdgeEnv::kDoNotEscape); + return env.LookupVariable("depfile"); +} + +string Edge::GetUnescapedRspfile() { + EdgeEnv env(this, EdgeEnv::kDoNotEscape); + return env.LookupVariable("rspfile"); +} + +void Edge::Dump(const char* prefix) const { + printf("%s[ ", prefix); + for (vector::const_iterator i = inputs_.begin(); + i != inputs_.end() && *i != NULL; ++i) { + printf("%s ", (*i)->path().c_str()); + } + printf("--%s-> ", rule_->name().c_str()); + for (vector::const_iterator i = outputs_.begin(); + i != outputs_.end() && *i != NULL; ++i) { + printf("%s ", (*i)->path().c_str()); + } + if (pool_) { + if (!pool_->name().empty()) { + printf("(in pool '%s')", pool_->name().c_str()); + } + } else { + printf("(null pool?)"); + } + printf("] 0x%p\n", this); +} + +bool Edge::is_phony() const { + return rule_ == &State::kPhonyRule; +} + +bool Edge::use_console() const { + return pool() == &State::kConsolePool; +} + +bool Edge::maybe_phonycycle_diagnostic() const { + // CMake 2.8.12.x and 3.0.x produced self-referencing phony rules + // of the form "build a: phony ... a ...". Restrict our + // "phonycycle" diagnostic option to the form it used. + return is_phony() && outputs_.size() == 1 && implicit_outs_ == 0 && + implicit_deps_ == 0; +} + +// static +string Node::PathDecanonicalized(const string& path, uint64_t slash_bits) { + string result = path; +#ifdef _WIN32 + uint64_t mask = 1; + for (char* c = &result[0]; (c = strchr(c, '/')) != NULL;) { + if (slash_bits & mask) + *c = '\\'; + c++; + mask <<= 1; + } +#endif + return result; +} + +void Node::Dump(const char* prefix) const { + printf("%s <%s 0x%p> mtime: %d%s, (:%s), ", + prefix, path().c_str(), this, + mtime(), mtime() ? "" : " (:missing)", + dirty() ? " dirty" : " clean"); + if (in_edge()) { + in_edge()->Dump("in-edge: "); + } else { + printf("no in-edge\n"); + } + printf(" out edges:\n"); + for (vector::const_iterator e = out_edges().begin(); + e != out_edges().end() && *e != NULL; ++e) { + (*e)->Dump(" +- "); + } +} + +bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) { + string deps_type = edge->GetBinding("deps"); + if (!deps_type.empty()) + return LoadDepsFromLog(edge, err); + + string depfile = edge->GetUnescapedDepfile(); + if (!depfile.empty()) + return LoadDepFile(edge, depfile, err); + + // No deps to load. + return true; +} + +bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path, + string* err) { + METRIC_RECORD("depfile load"); + // Read depfile content. Treat a missing depfile as empty. + string content; + switch (disk_interface_->ReadFile(path, &content, err)) { + case DiskInterface::Okay: + break; + case DiskInterface::NotFound: + err->clear(); + break; + case DiskInterface::OtherError: + *err = "loading '" + path + "': " + *err; + return false; + } + // On a missing depfile: return false and empty *err. + if (content.empty()) { + EXPLAIN("depfile '%s' is missing", path.c_str()); + return false; + } + + DepfileParser depfile; + string depfile_err; + if (!depfile.Parse(&content, &depfile_err)) { + *err = path + ": " + depfile_err; + return false; + } + + uint64_t unused; + if (!CanonicalizePath(const_cast(depfile.out_.str_), + &depfile.out_.len_, &unused, err)) { + *err = path + ": " + *err; + return false; + } + + // Check that this depfile matches the edge's output, if not return false to + // mark the edge as dirty. + Node* first_output = edge->outputs_[0]; + StringPiece opath = StringPiece(first_output->path()); + if (opath != depfile.out_) { + EXPLAIN("expected depfile '%s' to mention '%s', got '%s'", path.c_str(), + first_output->path().c_str(), depfile.out_.AsString().c_str()); + return false; + } + + // Preallocate space in edge->inputs_ to be filled in below. + vector::iterator implicit_dep = + PreallocateSpace(edge, depfile.ins_.size()); + + // Add all its in-edges. + for (vector::iterator i = depfile.ins_.begin(); + i != depfile.ins_.end(); ++i, ++implicit_dep) { + uint64_t slash_bits; + if (!CanonicalizePath(const_cast(i->str_), &i->len_, &slash_bits, + err)) + return false; + + Node* node = state_->GetNode(*i, slash_bits); + *implicit_dep = node; + node->AddOutEdge(edge); + CreatePhonyInEdge(node); + } + + return true; +} + +bool ImplicitDepLoader::LoadDepsFromLog(Edge* edge, string* err) { + // NOTE: deps are only supported for single-target edges. + Node* output = edge->outputs_[0]; + DepsLog::Deps* deps = deps_log_->GetDeps(output); + if (!deps) { + EXPLAIN("deps for '%s' are missing", output->path().c_str()); + return false; + } + + // Deps are invalid if the output is newer than the deps. + if (output->mtime() > deps->mtime) { + EXPLAIN("stored deps info out of date for '%s' (%d vs %d)", + output->path().c_str(), deps->mtime, output->mtime()); + return false; + } + + vector::iterator implicit_dep = + PreallocateSpace(edge, deps->node_count); + for (int i = 0; i < deps->node_count; ++i, ++implicit_dep) { + Node* node = deps->nodes[i]; + *implicit_dep = node; + node->AddOutEdge(edge); + CreatePhonyInEdge(node); + } + return true; +} + +vector::iterator ImplicitDepLoader::PreallocateSpace(Edge* edge, + int count) { + edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_, + (size_t)count, 0); + edge->implicit_deps_ += count; + return edge->inputs_.end() - edge->order_only_deps_ - count; +} + +void ImplicitDepLoader::CreatePhonyInEdge(Node* node) { + if (node->in_edge()) + return; + + Edge* phony_edge = state_->AddEdge(&State::kPhonyRule); + node->set_in_edge(phony_edge); + phony_edge->outputs_.push_back(node); + + // RecomputeDirty might not be called for phony_edge if a previous call + // to RecomputeDirty had caused the file to be stat'ed. Because previous + // invocations of RecomputeDirty would have seen this node without an + // input edge (and therefore ready), we have to set outputs_ready_ to true + // to avoid a potential stuck build. If we do call RecomputeDirty for + // this node, it will simply set outputs_ready_ to the correct value. + phony_edge->outputs_ready_ = true; +} diff --git a/src/3rdparty/ninja/src/graph.h b/src/3rdparty/ninja/src/graph.h new file mode 100644 index 00000000000..a8f0641d5fe --- /dev/null +++ b/src/3rdparty/ninja/src/graph.h @@ -0,0 +1,294 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_GRAPH_H_ +#define NINJA_GRAPH_H_ + +#include +#include +using namespace std; + +#include "eval_env.h" +#include "timestamp.h" +#include "util.h" + +struct BuildLog; +struct DiskInterface; +struct DepsLog; +struct Edge; +struct Node; +struct Pool; +struct State; + +/// Information about a node in the dependency graph: the file, whether +/// it's dirty, mtime, etc. +struct Node { + Node(const string& path, uint64_t slash_bits) + : path_(path), + slash_bits_(slash_bits), + mtime_(-1), + dirty_(false), + in_edge_(NULL), + id_(-1) {} + + /// Return false on error. + bool Stat(DiskInterface* disk_interface, string* err); + + /// Return false on error. + bool StatIfNecessary(DiskInterface* disk_interface, string* err) { + if (status_known()) + return true; + return Stat(disk_interface, err); + } + + /// Mark as not-yet-stat()ed and not dirty. + void ResetState() { + mtime_ = -1; + dirty_ = false; + } + + /// Mark the Node as already-stat()ed and missing. + void MarkMissing() { + mtime_ = 0; + } + + bool exists() const { + return mtime_ != 0; + } + + bool status_known() const { + return mtime_ != -1; + } + + const string& path() const { return path_; } + /// Get |path()| but use slash_bits to convert back to original slash styles. + string PathDecanonicalized() const { + return PathDecanonicalized(path_, slash_bits_); + } + static string PathDecanonicalized(const string& path, + uint64_t slash_bits); + uint64_t slash_bits() const { return slash_bits_; } + + TimeStamp mtime() const { return mtime_; } + + bool dirty() const { return dirty_; } + void set_dirty(bool dirty) { dirty_ = dirty; } + void MarkDirty() { dirty_ = true; } + + Edge* in_edge() const { return in_edge_; } + void set_in_edge(Edge* edge) { in_edge_ = edge; } + + int id() const { return id_; } + void set_id(int id) { id_ = id; } + + const vector& out_edges() const { return out_edges_; } + void AddOutEdge(Edge* edge) { out_edges_.push_back(edge); } + + void Dump(const char* prefix="") const; + +private: + string path_; + + /// Set bits starting from lowest for backslashes that were normalized to + /// forward slashes by CanonicalizePath. See |PathDecanonicalized|. + uint64_t slash_bits_; + + /// Possible values of mtime_: + /// -1: file hasn't been examined + /// 0: we looked, and file doesn't exist + /// >0: actual file's mtime + TimeStamp mtime_; + + /// Dirty is true when the underlying file is out-of-date. + /// But note that Edge::outputs_ready_ is also used in judging which + /// edges to build. + bool dirty_; + + /// The Edge that produces this Node, or NULL when there is no + /// known edge to produce it. + Edge* in_edge_; + + /// All Edges that use this Node as an input. + vector out_edges_; + + /// A dense integer id for the node, assigned and used by DepsLog. + int id_; +}; + +/// An edge in the dependency graph; links between Nodes using Rules. +struct Edge { + enum VisitMark { + VisitNone, + VisitInStack, + VisitDone + }; + + Edge() : rule_(NULL), pool_(NULL), env_(NULL), mark_(VisitNone), + outputs_ready_(false), deps_missing_(false), + implicit_deps_(0), order_only_deps_(0), implicit_outs_(0) {} + + /// Return true if all inputs' in-edges are ready. + bool AllInputsReady() const; + + /// Expand all variables in a command and return it as a string. + /// If incl_rsp_file is enabled, the string will also contain the + /// full contents of a response file (if applicable) + string EvaluateCommand(bool incl_rsp_file = false); + + /// Returns the shell-escaped value of |key|. + string GetBinding(const string& key); + bool GetBindingBool(const string& key); + + /// Like GetBinding("depfile"), but without shell escaping. + string GetUnescapedDepfile(); + /// Like GetBinding("rspfile"), but without shell escaping. + string GetUnescapedRspfile(); + + void Dump(const char* prefix="") const; + + const Rule* rule_; + Pool* pool_; + vector inputs_; + vector outputs_; + BindingEnv* env_; + VisitMark mark_; + bool outputs_ready_; + bool deps_missing_; + + const Rule& rule() const { return *rule_; } + Pool* pool() const { return pool_; } + int weight() const { return 1; } + bool outputs_ready() const { return outputs_ready_; } + + // There are three types of inputs. + // 1) explicit deps, which show up as $in on the command line; + // 2) implicit deps, which the target depends on implicitly (e.g. C headers), + // and changes in them cause the target to rebuild; + // 3) order-only deps, which are needed before the target builds but which + // don't cause the target to rebuild. + // These are stored in inputs_ in that order, and we keep counts of + // #2 and #3 when we need to access the various subsets. + int implicit_deps_; + int order_only_deps_; + bool is_implicit(size_t index) { + return index >= inputs_.size() - order_only_deps_ - implicit_deps_ && + !is_order_only(index); + } + bool is_order_only(size_t index) { + return index >= inputs_.size() - order_only_deps_; + } + + // There are two types of outputs. + // 1) explicit outs, which show up as $out on the command line; + // 2) implicit outs, which the target generates but are not part of $out. + // These are stored in outputs_ in that order, and we keep a count of + // #2 to use when we need to access the various subsets. + int implicit_outs_; + bool is_implicit_out(size_t index) const { + return index >= outputs_.size() - implicit_outs_; + } + + bool is_phony() const; + bool use_console() const; + bool maybe_phonycycle_diagnostic() const; +}; + + +/// ImplicitDepLoader loads implicit dependencies, as referenced via the +/// "depfile" attribute in build files. +struct ImplicitDepLoader { + ImplicitDepLoader(State* state, DepsLog* deps_log, + DiskInterface* disk_interface) + : state_(state), disk_interface_(disk_interface), deps_log_(deps_log) {} + + /// Load implicit dependencies for \a edge. + /// @return false on error (without filling \a err if info is just missing + // or out of date). + bool LoadDeps(Edge* edge, string* err); + + DepsLog* deps_log() const { + return deps_log_; + } + + private: + /// Load implicit dependencies for \a edge from a depfile attribute. + /// @return false on error (without filling \a err if info is just missing). + bool LoadDepFile(Edge* edge, const string& path, string* err); + + /// Load implicit dependencies for \a edge from the DepsLog. + /// @return false on error (without filling \a err if info is just missing). + bool LoadDepsFromLog(Edge* edge, string* err); + + /// Preallocate \a count spaces in the input array on \a edge, returning + /// an iterator pointing at the first new space. + vector::iterator PreallocateSpace(Edge* edge, int count); + + /// If we don't have a edge that generates this input already, + /// create one; this makes us not abort if the input is missing, + /// but instead will rebuild in that circumstance. + void CreatePhonyInEdge(Node* node); + + State* state_; + DiskInterface* disk_interface_; + DepsLog* deps_log_; +}; + + +/// DependencyScan manages the process of scanning the files in a graph +/// and updating the dirty/outputs_ready state of all the nodes and edges. +struct DependencyScan { + DependencyScan(State* state, BuildLog* build_log, DepsLog* deps_log, + DiskInterface* disk_interface) + : build_log_(build_log), + disk_interface_(disk_interface), + dep_loader_(state, deps_log, disk_interface) {} + + /// Update the |dirty_| state of the given node by inspecting its input edge. + /// Examine inputs, outputs, and command lines to judge whether an edge + /// needs to be re-run, and update outputs_ready_ and each outputs' |dirty_| + /// state accordingly. + /// Returns false on failure. + bool RecomputeDirty(Node* node, string* err); + + /// Recompute whether any output of the edge is dirty, if so sets |*dirty|. + /// Returns false on failure. + bool RecomputeOutputsDirty(Edge* edge, Node* most_recent_input, + bool* dirty, string* err); + + BuildLog* build_log() const { + return build_log_; + } + void set_build_log(BuildLog* log) { + build_log_ = log; + } + + DepsLog* deps_log() const { + return dep_loader_.deps_log(); + } + + private: + bool RecomputeDirty(Node* node, vector* stack, string* err); + bool VerifyDAG(Node* node, vector* stack, string* err); + + /// Recompute whether a given single output should be marked dirty. + /// Returns true if so. + bool RecomputeOutputDirty(Edge* edge, Node* most_recent_input, + const string& command, Node* output); + + BuildLog* build_log_; + DiskInterface* disk_interface_; + ImplicitDepLoader dep_loader_; +}; + +#endif // NINJA_GRAPH_H_ diff --git a/src/3rdparty/ninja/src/graph_test.cc b/src/3rdparty/ninja/src/graph_test.cc new file mode 100644 index 00000000000..422bc9a0533 --- /dev/null +++ b/src/3rdparty/ninja/src/graph_test.cc @@ -0,0 +1,481 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "graph.h" +#include "build.h" + +#include "test.h" + +struct GraphTest : public StateTestWithBuiltinRules { + GraphTest() : scan_(&state_, NULL, NULL, &fs_) {} + + VirtualFileSystem fs_; + DependencyScan scan_; +}; + +TEST_F(GraphTest, MissingImplicit) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat in | implicit\n")); + fs_.Create("in", ""); + fs_.Create("out", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err)); + ASSERT_EQ("", err); + + // A missing implicit dep *should* make the output dirty. + // (In fact, a build will fail.) + // This is a change from prior semantics of ninja. + EXPECT_TRUE(GetNode("out")->dirty()); +} + +TEST_F(GraphTest, ModifiedImplicit) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out: cat in | implicit\n")); + fs_.Create("in", ""); + fs_.Create("out", ""); + fs_.Tick(); + fs_.Create("implicit", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err)); + ASSERT_EQ("", err); + + // A modified implicit dep should make the output dirty. + EXPECT_TRUE(GetNode("out")->dirty()); +} + +TEST_F(GraphTest, FunkyMakefilePath) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule catdep\n" +" depfile = $out.d\n" +" command = cat $in > $out\n" +"build out.o: catdep foo.cc\n")); + fs_.Create("foo.cc", ""); + fs_.Create("out.o.d", "out.o: ./foo/../implicit.h\n"); + fs_.Create("out.o", ""); + fs_.Tick(); + fs_.Create("implicit.h", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err)); + ASSERT_EQ("", err); + + // implicit.h has changed, though our depfile refers to it with a + // non-canonical path; we should still find it. + EXPECT_TRUE(GetNode("out.o")->dirty()); +} + +TEST_F(GraphTest, ExplicitImplicit) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule catdep\n" +" depfile = $out.d\n" +" command = cat $in > $out\n" +"build implicit.h: cat data\n" +"build out.o: catdep foo.cc || implicit.h\n")); + fs_.Create("implicit.h", ""); + fs_.Create("foo.cc", ""); + fs_.Create("out.o.d", "out.o: implicit.h\n"); + fs_.Create("out.o", ""); + fs_.Tick(); + fs_.Create("data", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err)); + ASSERT_EQ("", err); + + // We have both an implicit and an explicit dep on implicit.h. + // The implicit dep should "win" (in the sense that it should cause + // the output to be dirty). + EXPECT_TRUE(GetNode("out.o")->dirty()); +} + +TEST_F(GraphTest, ImplicitOutputParse) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + + Edge* edge = GetNode("out")->in_edge(); + EXPECT_EQ(2, edge->outputs_.size()); + EXPECT_EQ("out", edge->outputs_[0]->path()); + EXPECT_EQ("out.imp", edge->outputs_[1]->path()); + EXPECT_EQ(1, edge->implicit_outs_); + EXPECT_EQ(edge, GetNode("out.imp")->in_edge()); +} + +TEST_F(GraphTest, ImplicitOutputMissing) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + fs_.Create("in", ""); + fs_.Create("out", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out")->dirty()); + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + +TEST_F(GraphTest, ImplicitOutputOutOfDate) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + fs_.Create("out.imp", ""); + fs_.Tick(); + fs_.Create("in", ""); + fs_.Create("out", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out")->dirty()); + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + +TEST_F(GraphTest, ImplicitOutputOnlyParse) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build | out.imp: cat in\n")); + + Edge* edge = GetNode("out.imp")->in_edge(); + EXPECT_EQ(1, edge->outputs_.size()); + EXPECT_EQ("out.imp", edge->outputs_[0]->path()); + EXPECT_EQ(1, edge->implicit_outs_); + EXPECT_EQ(edge, GetNode("out.imp")->in_edge()); +} + +TEST_F(GraphTest, ImplicitOutputOnlyMissing) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build | out.imp: cat in\n")); + fs_.Create("in", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + +TEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build | out.imp: cat in\n")); + fs_.Create("out.imp", ""); + fs_.Tick(); + fs_.Create("in", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + +TEST_F(GraphTest, PathWithCurrentDirectory) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule catdep\n" +" depfile = $out.d\n" +" command = cat $in > $out\n" +"build ./out.o: catdep ./foo.cc\n")); + fs_.Create("foo.cc", ""); + fs_.Create("out.o.d", "out.o: foo.cc\n"); + fs_.Create("out.o", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err)); + ASSERT_EQ("", err); + + EXPECT_FALSE(GetNode("out.o")->dirty()); +} + +TEST_F(GraphTest, RootNodes) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out1: cat in1\n" +"build mid1: cat in1\n" +"build out2: cat mid1\n" +"build out3 out4: cat mid1\n")); + + string err; + vector root_nodes = state_.RootNodes(&err); + EXPECT_EQ(4u, root_nodes.size()); + for (size_t i = 0; i < root_nodes.size(); ++i) { + string name = root_nodes[i]->path(); + EXPECT_EQ("out", name.substr(0, 3)); + } +} + +TEST_F(GraphTest, VarInOutPathEscaping) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build a$ b: cat no'space with$ space$$ no\"space2\n")); + + Edge* edge = GetNode("a b")->in_edge(); +#if _WIN32 + EXPECT_EQ("cat no'space \"with space$\" \"no\\\"space2\" > \"a b\"", + edge->EvaluateCommand()); +#else + EXPECT_EQ("cat 'no'\\''space' 'with space$' 'no\"space2' > 'a b'", + edge->EvaluateCommand()); +#endif +} + +// Regression test for https://github.com/ninja-build/ninja/issues/380 +TEST_F(GraphTest, DepfileWithCanonicalizablePath) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule catdep\n" +" depfile = $out.d\n" +" command = cat $in > $out\n" +"build ./out.o: catdep ./foo.cc\n")); + fs_.Create("foo.cc", ""); + fs_.Create("out.o.d", "out.o: bar/../foo.cc\n"); + fs_.Create("out.o", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err)); + ASSERT_EQ("", err); + + EXPECT_FALSE(GetNode("out.o")->dirty()); +} + +// Regression test for https://github.com/ninja-build/ninja/issues/404 +TEST_F(GraphTest, DepfileRemoved) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule catdep\n" +" depfile = $out.d\n" +" command = cat $in > $out\n" +"build ./out.o: catdep ./foo.cc\n")); + fs_.Create("foo.h", ""); + fs_.Create("foo.cc", ""); + fs_.Tick(); + fs_.Create("out.o.d", "out.o: foo.h\n"); + fs_.Create("out.o", ""); + + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err)); + ASSERT_EQ("", err); + EXPECT_FALSE(GetNode("out.o")->dirty()); + + state_.Reset(); + fs_.RemoveFile("out.o.d"); + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(GetNode("out.o")->dirty()); +} + +// Check that rule-level variables are in scope for eval. +TEST_F(GraphTest, RuleVariablesInScope) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule r\n" +" depfile = x\n" +" command = depfile is $depfile\n" +"build out: r in\n")); + Edge* edge = GetNode("out")->in_edge(); + EXPECT_EQ("depfile is x", edge->EvaluateCommand()); +} + +// Check that build statements can override rule builtins like depfile. +TEST_F(GraphTest, DepfileOverride) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule r\n" +" depfile = x\n" +" command = unused\n" +"build out: r in\n" +" depfile = y\n")); + Edge* edge = GetNode("out")->in_edge(); + EXPECT_EQ("y", edge->GetBinding("depfile")); +} + +// Check that overridden values show up in expansion of rule-level bindings. +TEST_F(GraphTest, DepfileOverrideParent) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule r\n" +" depfile = x\n" +" command = depfile is $depfile\n" +"build out: r in\n" +" depfile = y\n")); + Edge* edge = GetNode("out")->in_edge(); + EXPECT_EQ("depfile is y", edge->GetBinding("command")); +} + +// Verify that building a nested phony rule prints "no work to do" +TEST_F(GraphTest, NestedPhonyPrintsDone) { + AssertParse(&state_, +"build n1: phony \n" +"build n2: phony n1\n" + ); + string err; + EXPECT_TRUE(scan_.RecomputeDirty(GetNode("n2"), &err)); + ASSERT_EQ("", err); + + Plan plan_; + EXPECT_TRUE(plan_.AddTarget(GetNode("n2"), &err)); + ASSERT_EQ("", err); + + EXPECT_EQ(0, plan_.command_edge_count()); + ASSERT_FALSE(plan_.more_to_do()); +} + +TEST_F(GraphTest, PhonySelfReferenceError) { + ManifestParserOptions parser_opts; + parser_opts.phony_cycle_action_ = kPhonyCycleActionError; + AssertParse(&state_, +"build a: phony a\n", + parser_opts); + + string err; + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err)); + ASSERT_EQ("dependency cycle: a -> a [-w phonycycle=err]", err); +} + +TEST_F(GraphTest, DependencyCycle) { + AssertParse(&state_, +"build out: cat mid\n" +"build mid: cat in\n" +"build in: cat pre\n" +"build pre: cat out\n"); + + string err; + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), &err)); + ASSERT_EQ("dependency cycle: out -> mid -> in -> pre -> out", err); +} + +TEST_F(GraphTest, CycleInEdgesButNotInNodes1) { + string err; + AssertParse(&state_, +"build a b: cat a\n"); + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err)); + ASSERT_EQ("dependency cycle: a -> a", err); +} + +TEST_F(GraphTest, CycleInEdgesButNotInNodes2) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build b a: cat a\n")); + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err)); + ASSERT_EQ("dependency cycle: a -> a", err); +} + +TEST_F(GraphTest, CycleInEdgesButNotInNodes3) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build a b: cat c\n" +"build c: cat a\n")); + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err)); + ASSERT_EQ("dependency cycle: a -> c -> a", err); +} + +TEST_F(GraphTest, CycleInEdgesButNotInNodes4) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build d: cat c\n" +"build c: cat b\n" +"build b: cat a\n" +"build a e: cat d\n" +"build f: cat e\n")); + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("f"), &err)); + ASSERT_EQ("dependency cycle: a -> d -> c -> b -> a", err); +} + +// Verify that cycles in graphs with multiple outputs are handled correctly +// in RecomputeDirty() and don't cause deps to be loaded multiple times. +TEST_F(GraphTest, CycleWithLengthZeroFromDepfile) { + AssertParse(&state_, +"rule deprule\n" +" depfile = dep.d\n" +" command = unused\n" +"build a b: deprule\n" + ); + fs_.Create("dep.d", "a: b\n"); + + string err; + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err)); + ASSERT_EQ("dependency cycle: b -> b", err); + + // Despite the depfile causing edge to be a cycle (it has outputs a and b, + // but the depfile also adds b as an input), the deps should have been loaded + // only once: + Edge* edge = GetNode("a")->in_edge(); + EXPECT_EQ(1, edge->inputs_.size()); + EXPECT_EQ("b", edge->inputs_[0]->path()); +} + +// Like CycleWithLengthZeroFromDepfile but with a higher cycle length. +TEST_F(GraphTest, CycleWithLengthOneFromDepfile) { + AssertParse(&state_, +"rule deprule\n" +" depfile = dep.d\n" +" command = unused\n" +"rule r\n" +" command = unused\n" +"build a b: deprule\n" +"build c: r b\n" + ); + fs_.Create("dep.d", "a: c\n"); + + string err; + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err)); + ASSERT_EQ("dependency cycle: b -> c -> b", err); + + // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b, + // but c's in_edge has b as input but the depfile also adds |edge| as + // output)), the deps should have been loaded only once: + Edge* edge = GetNode("a")->in_edge(); + EXPECT_EQ(1, edge->inputs_.size()); + EXPECT_EQ("c", edge->inputs_[0]->path()); +} + +// Like CycleWithLengthOneFromDepfile but building a node one hop away from +// the cycle. +TEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) { + AssertParse(&state_, +"rule deprule\n" +" depfile = dep.d\n" +" command = unused\n" +"rule r\n" +" command = unused\n" +"build a b: deprule\n" +"build c: r b\n" +"build d: r a\n" + ); + fs_.Create("dep.d", "a: c\n"); + + string err; + EXPECT_FALSE(scan_.RecomputeDirty(GetNode("d"), &err)); + ASSERT_EQ("dependency cycle: b -> c -> b", err); + + // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b, + // but c's in_edge has b as input but the depfile also adds |edge| as + // output)), the deps should have been loaded only once: + Edge* edge = GetNode("a")->in_edge(); + EXPECT_EQ(1, edge->inputs_.size()); + EXPECT_EQ("c", edge->inputs_[0]->path()); +} + +#ifdef _WIN32 +TEST_F(GraphTest, Decanonicalize) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out\\out1: cat src\\in1\n" +"build out\\out2/out3\\out4: cat mid1\n" +"build out3 out4\\foo: cat mid1\n")); + + string err; + vector root_nodes = state_.RootNodes(&err); + EXPECT_EQ(4u, root_nodes.size()); + EXPECT_EQ(root_nodes[0]->path(), "out/out1"); + EXPECT_EQ(root_nodes[1]->path(), "out/out2/out3/out4"); + EXPECT_EQ(root_nodes[2]->path(), "out3"); + EXPECT_EQ(root_nodes[3]->path(), "out4/foo"); + EXPECT_EQ(root_nodes[0]->PathDecanonicalized(), "out\\out1"); + EXPECT_EQ(root_nodes[1]->PathDecanonicalized(), "out\\out2/out3\\out4"); + EXPECT_EQ(root_nodes[2]->PathDecanonicalized(), "out3"); + EXPECT_EQ(root_nodes[3]->PathDecanonicalized(), "out4\\foo"); +} +#endif diff --git a/src/3rdparty/ninja/src/graphviz.cc b/src/3rdparty/ninja/src/graphviz.cc new file mode 100644 index 00000000000..dce8b322563 --- /dev/null +++ b/src/3rdparty/ninja/src/graphviz.cc @@ -0,0 +1,80 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "graphviz.h" + +#include +#include + +#include "graph.h" + +void GraphViz::AddTarget(Node* node) { + if (visited_nodes_.find(node) != visited_nodes_.end()) + return; + + string pathstr = node->path(); + replace(pathstr.begin(), pathstr.end(), '\\', '/'); + printf("\"%p\" [label=\"%s\"]\n", node, pathstr.c_str()); + visited_nodes_.insert(node); + + Edge* edge = node->in_edge(); + + if (!edge) { + // Leaf node. + // Draw as a rect? + return; + } + + if (visited_edges_.find(edge) != visited_edges_.end()) + return; + visited_edges_.insert(edge); + + if (edge->inputs_.size() == 1 && edge->outputs_.size() == 1) { + // Can draw simply. + // Note extra space before label text -- this is cosmetic and feels + // like a graphviz bug. + printf("\"%p\" -> \"%p\" [label=\" %s\"]\n", + edge->inputs_[0], edge->outputs_[0], edge->rule_->name().c_str()); + } else { + printf("\"%p\" [label=\"%s\", shape=ellipse]\n", + edge, edge->rule_->name().c_str()); + for (vector::iterator out = edge->outputs_.begin(); + out != edge->outputs_.end(); ++out) { + printf("\"%p\" -> \"%p\"\n", edge, *out); + } + for (vector::iterator in = edge->inputs_.begin(); + in != edge->inputs_.end(); ++in) { + const char* order_only = ""; + if (edge->is_order_only(in - edge->inputs_.begin())) + order_only = " style=dotted"; + printf("\"%p\" -> \"%p\" [arrowhead=none%s]\n", (*in), edge, order_only); + } + } + + for (vector::iterator in = edge->inputs_.begin(); + in != edge->inputs_.end(); ++in) { + AddTarget(*in); + } +} + +void GraphViz::Start() { + printf("digraph ninja {\n"); + printf("rankdir=\"LR\"\n"); + printf("node [fontsize=10, shape=box, height=0.25]\n"); + printf("edge [fontsize=10]\n"); +} + +void GraphViz::Finish() { + printf("}\n"); +} diff --git a/src/3rdparty/ninja/src/graphviz.h b/src/3rdparty/ninja/src/graphviz.h new file mode 100644 index 00000000000..408496ddf5b --- /dev/null +++ b/src/3rdparty/ninja/src/graphviz.h @@ -0,0 +1,33 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_GRAPHVIZ_H_ +#define NINJA_GRAPHVIZ_H_ + +#include + +struct Node; +struct Edge; + +/// Runs the process of creating GraphViz .dot file output. +struct GraphViz { + void Start(); + void AddTarget(Node* node); + void Finish(); + + std::set visited_nodes_; + std::set visited_edges_; +}; + +#endif // NINJA_GRAPHVIZ_H_ diff --git a/src/3rdparty/ninja/src/hash_collision_bench.cc b/src/3rdparty/ninja/src/hash_collision_bench.cc new file mode 100644 index 00000000000..ff947dca60c --- /dev/null +++ b/src/3rdparty/ninja/src/hash_collision_bench.cc @@ -0,0 +1,63 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "build_log.h" + +#include +using namespace std; + +#include +#include + +int random(int low, int high) { + return int(low + (rand() / double(RAND_MAX)) * (high - low) + 0.5); +} + +void RandomCommand(char** s) { + int len = random(5, 100); + *s = new char[len]; + for (int i = 0; i < len; ++i) + (*s)[i] = (char)random(32, 127); +} + +int main() { + const int N = 20 * 1000 * 1000; + + // Leak these, else 10% of the runtime is spent destroying strings. + char** commands = new char*[N]; + pair* hashes = new pair[N]; + + srand((int)time(NULL)); + + for (int i = 0; i < N; ++i) { + RandomCommand(&commands[i]); + hashes[i] = make_pair(BuildLog::LogEntry::HashCommand(commands[i]), i); + } + + sort(hashes, hashes + N); + + int collision_count = 0; + for (int i = 1; i < N; ++i) { + if (hashes[i - 1].first == hashes[i].first) { + if (strcmp(commands[hashes[i - 1].second], + commands[hashes[i].second]) != 0) { + printf("collision!\n string 1: '%s'\n string 2: '%s'\n", + commands[hashes[i - 1].second], + commands[hashes[i].second]); + collision_count++; + } + } + } + printf("\n\n%d collisions after %d runs\n", collision_count, N); +} diff --git a/src/3rdparty/ninja/src/hash_map.h b/src/3rdparty/ninja/src/hash_map.h new file mode 100644 index 00000000000..a91aeb99600 --- /dev/null +++ b/src/3rdparty/ninja/src/hash_map.h @@ -0,0 +1,120 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_MAP_H_ +#define NINJA_MAP_H_ + +#include +#include +#include "string_piece.h" + +// MurmurHash2, by Austin Appleby +static inline +unsigned int MurmurHash2(const void* key, size_t len) { + static const unsigned int seed = 0xDECAFBAD; + const unsigned int m = 0x5bd1e995; + const int r = 24; + unsigned int h = seed ^ len; + const unsigned char* data = (const unsigned char*)key; + while (len >= 4) { + unsigned int k; + memcpy(&k, data, sizeof k); + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + data += 4; + len -= 4; + } + switch (len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; +} + +#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900) +#include + +namespace std { +template<> +struct hash { + typedef StringPiece argument_type; + typedef size_t result_type; + + size_t operator()(StringPiece key) const { + return MurmurHash2(key.str_, key.len_); + } +}; +} + +#elif defined(_MSC_VER) +#include + +using stdext::hash_map; +using stdext::hash_compare; + +struct StringPieceCmp : public hash_compare { + size_t operator()(const StringPiece& key) const { + return MurmurHash2(key.str_, key.len_); + } + bool operator()(const StringPiece& a, const StringPiece& b) const { + int cmp = memcmp(a.str_, b.str_, min(a.len_, b.len_)); + if (cmp < 0) { + return true; + } else if (cmp > 0) { + return false; + } else { + return a.len_ < b.len_; + } + } +}; + +#else +#include + +using __gnu_cxx::hash_map; + +namespace __gnu_cxx { +template<> +struct hash { + size_t operator()(StringPiece key) const { + return MurmurHash2(key.str_, key.len_); + } +}; +} +#endif + +/// A template for hash_maps keyed by a StringPiece whose string is +/// owned externally (typically by the values). Use like: +/// ExternalStringHash::Type foos; to make foos into a hash +/// mapping StringPiece => Foo*. +template +struct ExternalStringHashMap { +#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900) + typedef std::unordered_map Type; +#elif defined(_MSC_VER) + typedef hash_map Type; +#else + typedef hash_map Type; +#endif +}; + +#endif // NINJA_MAP_H_ diff --git a/src/3rdparty/ninja/src/includes_normalize-win32.cc b/src/3rdparty/ninja/src/includes_normalize-win32.cc new file mode 100644 index 00000000000..459329bc99d --- /dev/null +++ b/src/3rdparty/ninja/src/includes_normalize-win32.cc @@ -0,0 +1,176 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "includes_normalize.h" + +#include "string_piece.h" +#include "string_piece_util.h" +#include "util.h" + +#include +#include +#include + +#include + +namespace { + +bool IsPathSeparator(char c) { + return c == '/' || c == '\\'; +} + +// Return true if paths a and b are on the same windows drive. +// Return false if this funcation cannot check +// whether or not on the same windows drive. +bool SameDriveFast(StringPiece a, StringPiece b) { + if (a.size() < 3 || b.size() < 3) { + return false; + } + + if (!islatinalpha(a[0]) || !islatinalpha(b[0])) { + return false; + } + + if (ToLowerASCII(a[0]) != ToLowerASCII(b[0])) { + return false; + } + + if (a[1] != ':' || b[1] != ':') { + return false; + } + + return IsPathSeparator(a[2]) && IsPathSeparator(b[2]); +} + +// Return true if paths a and b are on the same Windows drive. +bool SameDrive(StringPiece a, StringPiece b) { + if (SameDriveFast(a, b)) { + return true; + } + + char a_absolute[_MAX_PATH]; + char b_absolute[_MAX_PATH]; + GetFullPathName(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL); + GetFullPathName(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL); + char a_drive[_MAX_DIR]; + char b_drive[_MAX_DIR]; + _splitpath(a_absolute, a_drive, NULL, NULL, NULL); + _splitpath(b_absolute, b_drive, NULL, NULL, NULL); + return _stricmp(a_drive, b_drive) == 0; +} + +// Check path |s| is FullPath style returned by GetFullPathName. +// This ignores difference of path separator. +// This is used not to call very slow GetFullPathName API. +bool IsFullPathName(StringPiece s) { + if (s.size() < 3 || + !islatinalpha(s[0]) || + s[1] != ':' || + !IsPathSeparator(s[2])) { + return false; + } + + // Check "." or ".." is contained in path. + for (size_t i = 2; i < s.size(); ++i) { + if (!IsPathSeparator(s[i])) { + continue; + } + + // Check ".". + if (i + 1 < s.size() && s[i+1] == '.' && + (i + 2 >= s.size() || IsPathSeparator(s[i+2]))) { + return false; + } + + // Check "..". + if (i + 2 < s.size() && s[i+1] == '.' && s[i+2] == '.' && + (i + 3 >= s.size() || IsPathSeparator(s[i+3]))) { + return false; + } + } + + return true; +} + +} // anonymous namespace + +IncludesNormalize::IncludesNormalize(const string& relative_to) { + relative_to_ = AbsPath(relative_to); + split_relative_to_ = SplitStringPiece(relative_to_, '/'); +} + +string IncludesNormalize::AbsPath(StringPiece s) { + if (IsFullPathName(s)) { + string result = s.AsString(); + for (size_t i = 0; i < result.size(); ++i) { + if (result[i] == '\\') { + result[i] = '/'; + } + } + return result; + } + + char result[_MAX_PATH]; + GetFullPathName(s.AsString().c_str(), sizeof(result), result, NULL); + for (char* c = result; *c; ++c) + if (*c == '\\') + *c = '/'; + return result; +} + +string IncludesNormalize::Relativize( + StringPiece path, const vector& start_list) { + string abs_path = AbsPath(path); + vector path_list = SplitStringPiece(abs_path, '/'); + int i; + for (i = 0; i < static_cast(min(start_list.size(), path_list.size())); + ++i) { + if (!EqualsCaseInsensitiveASCII(start_list[i], path_list[i])) { + break; + } + } + + vector rel_list; + rel_list.reserve(start_list.size() - i + path_list.size() - i); + for (int j = 0; j < static_cast(start_list.size() - i); ++j) + rel_list.push_back(".."); + for (int j = i; j < static_cast(path_list.size()); ++j) + rel_list.push_back(path_list[j]); + if (rel_list.size() == 0) + return "."; + return JoinStringPiece(rel_list, '/'); +} + +bool IncludesNormalize::Normalize(const string& input, + string* result, string* err) const { + char copy[_MAX_PATH + 1]; + size_t len = input.size(); + if (len > _MAX_PATH) { + *err = "path too long"; + return false; + } + strncpy(copy, input.c_str(), input.size() + 1); + uint64_t slash_bits; + if (!CanonicalizePath(copy, &len, &slash_bits, err)) + return false; + StringPiece partially_fixed(copy, len); + string abs_input = AbsPath(partially_fixed); + + if (!SameDrive(abs_input, relative_to_)) { + *result = partially_fixed.AsString(); + return true; + } + *result = Relativize(abs_input, split_relative_to_); + return true; +} diff --git a/src/3rdparty/ninja/src/includes_normalize.h b/src/3rdparty/ninja/src/includes_normalize.h new file mode 100644 index 00000000000..3811e53840b --- /dev/null +++ b/src/3rdparty/ninja/src/includes_normalize.h @@ -0,0 +1,39 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +using namespace std; + +struct StringPiece; + +/// Utility functions for normalizing include paths on Windows. +/// TODO: this likely duplicates functionality of CanonicalizePath; refactor. +struct IncludesNormalize { + /// Normalize path relative to |relative_to|. + IncludesNormalize(const string& relative_to); + + // Internal utilities made available for testing, maybe useful otherwise. + static string AbsPath(StringPiece s); + static string Relativize(StringPiece path, + const vector& start_list); + + /// Normalize by fixing slashes style, fixing redundant .. and . and makes the + /// path |input| relative to |this->relative_to_| and store to |result|. + bool Normalize(const string& input, string* result, string* err) const; + + private: + string relative_to_; + vector split_relative_to_; +}; diff --git a/src/3rdparty/ninja/src/includes_normalize_test.cc b/src/3rdparty/ninja/src/includes_normalize_test.cc new file mode 100644 index 00000000000..eac36fd2d79 --- /dev/null +++ b/src/3rdparty/ninja/src/includes_normalize_test.cc @@ -0,0 +1,140 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "includes_normalize.h" + +#include + +#include + +#include "string_piece_util.h" +#include "test.h" +#include "util.h" + +namespace { + +string GetCurDir() { + char buf[_MAX_PATH]; + _getcwd(buf, sizeof(buf)); + vector parts = SplitStringPiece(buf, '\\'); + return parts[parts.size() - 1].AsString(); +} + +string NormalizeAndCheckNoError(const string& input) { + string result, err; + IncludesNormalize normalizer("."); + EXPECT_TRUE(normalizer.Normalize(input, &result, &err)); + EXPECT_EQ("", err); + return result; +} + +string NormalizeRelativeAndCheckNoError(const string& input, + const string& relative_to) { + string result, err; + IncludesNormalize normalizer(relative_to); + EXPECT_TRUE(normalizer.Normalize(input, &result, &err)); + EXPECT_EQ("", err); + return result; +} + +} // namespace + +TEST(IncludesNormalize, Simple) { + EXPECT_EQ("b", NormalizeAndCheckNoError("a\\..\\b")); + EXPECT_EQ("b", NormalizeAndCheckNoError("a\\../b")); + EXPECT_EQ("a/b", NormalizeAndCheckNoError("a\\.\\b")); + EXPECT_EQ("a/b", NormalizeAndCheckNoError("a\\./b")); +} + +TEST(IncludesNormalize, WithRelative) { + string currentdir = GetCurDir(); + EXPECT_EQ("c", NormalizeRelativeAndCheckNoError("a/b/c", "a/b")); + EXPECT_EQ("a", NormalizeAndCheckNoError(IncludesNormalize::AbsPath("a"))); + EXPECT_EQ(string("../") + currentdir + string("/a"), + NormalizeRelativeAndCheckNoError("a", "../b")); + EXPECT_EQ(string("../") + currentdir + string("/a/b"), + NormalizeRelativeAndCheckNoError("a/b", "../c")); + EXPECT_EQ("../../a", NormalizeRelativeAndCheckNoError("a", "b/c")); + EXPECT_EQ(".", NormalizeRelativeAndCheckNoError("a", "a")); +} + +TEST(IncludesNormalize, Case) { + EXPECT_EQ("b", NormalizeAndCheckNoError("Abc\\..\\b")); + EXPECT_EQ("BdEf", NormalizeAndCheckNoError("Abc\\..\\BdEf")); + EXPECT_EQ("A/b", NormalizeAndCheckNoError("A\\.\\b")); + EXPECT_EQ("a/b", NormalizeAndCheckNoError("a\\./b")); + EXPECT_EQ("A/B", NormalizeAndCheckNoError("A\\.\\B")); + EXPECT_EQ("A/B", NormalizeAndCheckNoError("A\\./B")); +} + +TEST(IncludesNormalize, DifferentDrive) { + EXPECT_EQ("stuff.h", + NormalizeRelativeAndCheckNoError("p:\\vs08\\stuff.h", "p:\\vs08")); + EXPECT_EQ("stuff.h", + NormalizeRelativeAndCheckNoError("P:\\Vs08\\stuff.h", "p:\\vs08")); + EXPECT_EQ("p:/vs08/stuff.h", + NormalizeRelativeAndCheckNoError("p:\\vs08\\stuff.h", "c:\\vs08")); + EXPECT_EQ("P:/vs08/stufF.h", NormalizeRelativeAndCheckNoError( + "P:\\vs08\\stufF.h", "D:\\stuff/things")); + EXPECT_EQ("P:/vs08/stuff.h", NormalizeRelativeAndCheckNoError( + "P:/vs08\\stuff.h", "D:\\stuff/things")); + EXPECT_EQ("P:/wee/stuff.h", + NormalizeRelativeAndCheckNoError("P:/vs08\\../wee\\stuff.h", + "D:\\stuff/things")); +} + +TEST(IncludesNormalize, LongInvalidPath) { + const char kLongInputString[] = + "C:\\Program Files (x86)\\Microsoft Visual Studio " + "12.0\\VC\\INCLUDEwarning #31001: The dll for reading and writing the " + "pdb (for example, mspdb110.dll) could not be found on your path. This " + "is usually a configuration error. Compilation will continue using /Z7 " + "instead of /Zi, but expect a similar error when you link your program."; + // Too long, won't be canonicalized. Ensure doesn't crash. + string result, err; + IncludesNormalize normalizer("."); + EXPECT_FALSE( + normalizer.Normalize(kLongInputString, &result, &err)); + EXPECT_EQ("path too long", err); + + + // Construct max size path having cwd prefix. + // kExactlyMaxPath = "$cwd\\a\\aaaa...aaaa\0"; + char kExactlyMaxPath[_MAX_PATH + 1]; + ASSERT_NE(_getcwd(kExactlyMaxPath, sizeof kExactlyMaxPath), NULL); + + int cwd_len = strlen(kExactlyMaxPath); + ASSERT_LE(cwd_len + 3 + 1, _MAX_PATH) + kExactlyMaxPath[cwd_len] = '\\'; + kExactlyMaxPath[cwd_len + 1] = 'a'; + kExactlyMaxPath[cwd_len + 2] = '\\'; + + kExactlyMaxPath[cwd_len + 3] = 'a'; + + for (int i = cwd_len + 4; i < _MAX_PATH; ++i) { + if (i > cwd_len + 4 && i < _MAX_PATH - 1 && i % 10 == 0) + kExactlyMaxPath[i] = '\\'; + else + kExactlyMaxPath[i] = 'a'; + } + + kExactlyMaxPath[_MAX_PATH] = '\0'; + EXPECT_EQ(strlen(kExactlyMaxPath), _MAX_PATH); + + string forward_slashes(kExactlyMaxPath); + replace(forward_slashes.begin(), forward_slashes.end(), '\\', '/'); + // Make sure a path that's exactly _MAX_PATH long is canonicalized. + EXPECT_EQ(forward_slashes.substr(cwd_len + 1), + NormalizeAndCheckNoError(kExactlyMaxPath)); +} diff --git a/src/3rdparty/ninja/src/inline.sh b/src/3rdparty/ninja/src/inline.sh new file mode 100755 index 00000000000..fa282fabd34 --- /dev/null +++ b/src/3rdparty/ninja/src/inline.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright 2001 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This quick script converts a text file into an #include-able header. +# It expects the name of the variable as its first argument, and reads +# stdin and writes stdout. + +varname="$1" +echo "const char $varname[] =" +od -t x1 -A n -v | sed -e 's|[ \t]||g; s|..|\\x&|g; s|^|"|; s|$|"|' +echo ";" + diff --git a/src/3rdparty/ninja/src/lexer.cc b/src/3rdparty/ninja/src/lexer.cc new file mode 100644 index 00000000000..37b867885c8 --- /dev/null +++ b/src/3rdparty/ninja/src/lexer.cc @@ -0,0 +1,867 @@ +/* Generated by re2c 0.13.5 */ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lexer.h" + +#include + +#include "eval_env.h" +#include "util.h" + +bool Lexer::Error(const string& message, string* err) { + // Compute line/column. + int line = 1; + const char* context = input_.str_; + for (const char* p = input_.str_; p < last_token_; ++p) { + if (*p == '\n') { + ++line; + context = p + 1; + } + } + int col = last_token_ ? (int)(last_token_ - context) : 0; + + char buf[1024]; + snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); + *err = buf; + *err += message + "\n"; + + // Add some context to the message. + const int kTruncateColumn = 72; + if (col > 0 && col < kTruncateColumn) { + int len; + bool truncated = true; + for (len = 0; len < kTruncateColumn; ++len) { + if (context[len] == 0 || context[len] == '\n') { + truncated = false; + break; + } + } + *err += string(context, len); + if (truncated) + *err += "..."; + *err += "\n"; + *err += string(col, ' '); + *err += "^ near here"; + } + + return false; +} + +Lexer::Lexer(const char* input) { + Start("input", input); +} + +void Lexer::Start(StringPiece filename, StringPiece input) { + filename_ = filename; + input_ = input; + ofs_ = input_.str_; + last_token_ = NULL; +} + +const char* Lexer::TokenName(Token t) { + switch (t) { + case ERROR: return "lexing error"; + case BUILD: return "'build'"; + case COLON: return "':'"; + case DEFAULT: return "'default'"; + case EQUALS: return "'='"; + case IDENT: return "identifier"; + case INCLUDE: return "'include'"; + case INDENT: return "indent"; + case NEWLINE: return "newline"; + case PIPE2: return "'||'"; + case PIPE: return "'|'"; + case POOL: return "'pool'"; + case RULE: return "'rule'"; + case SUBNINJA: return "'subninja'"; + case TEOF: return "eof"; + } + return NULL; // not reached +} + +const char* Lexer::TokenErrorHint(Token expected) { + switch (expected) { + case COLON: + return " ($ also escapes ':')"; + default: + return ""; + } +} + +string Lexer::DescribeLastError() { + if (last_token_) { + switch (last_token_[0]) { + case '\t': + return "tabs are not allowed, use spaces"; + } + } + return "lexing error"; +} + +void Lexer::UnreadToken() { + ofs_ = last_token_; +} + +Lexer::Token Lexer::ReadToken() { + const char* p = ofs_; + const char* q; + const char* start; + Lexer::Token token; + for (;;) { + start = p; + +{ + unsigned char yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { + 0, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 0, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 192, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 96, 96, 64, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 64, 64, 64, 64, 64, 64, + 64, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 64, 64, 64, 64, 96, + 64, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }; + + yych = *p; + if (yych <= 'Z') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy23; + if (yych == '\n') goto yy7; + goto yy25; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy6; + goto yy25; + } else { + if (yych <= ' ') goto yy2; + if (yych <= '"') goto yy25; + goto yy4; + } + } + } else { + if (yych <= '9') { + if (yych <= ',') goto yy25; + if (yych == '/') goto yy25; + goto yy22; + } else { + if (yych <= '<') { + if (yych <= ':') goto yy16; + goto yy25; + } else { + if (yych <= '=') goto yy14; + if (yych <= '@') goto yy25; + goto yy22; + } + } + } + } else { + if (yych <= 'i') { + if (yych <= 'a') { + if (yych == '_') goto yy22; + if (yych <= '`') goto yy25; + goto yy22; + } else { + if (yych <= 'c') { + if (yych <= 'b') goto yy9; + goto yy22; + } else { + if (yych <= 'd') goto yy13; + if (yych <= 'h') goto yy22; + goto yy20; + } + } + } else { + if (yych <= 'r') { + if (yych == 'p') goto yy11; + if (yych <= 'q') goto yy22; + goto yy12; + } else { + if (yych <= 'z') { + if (yych <= 's') goto yy21; + goto yy22; + } else { + if (yych == '|') goto yy18; + goto yy25; + } + } + } + } +yy2: + yyaccept = 0; + yych = *(q = ++p); + goto yy73; +yy3: + { token = INDENT; break; } +yy4: + yyaccept = 1; + yych = *(q = ++p); + if (yych >= 0x01) goto yy68; +yy5: + { token = ERROR; break; } +yy6: + yych = *++p; + if (yych == '\n') goto yy65; + goto yy5; +yy7: + ++p; +yy8: + { token = NEWLINE; break; } +yy9: + ++p; + if ((yych = *p) == 'u') goto yy60; + goto yy27; +yy10: + { token = IDENT; break; } +yy11: + yych = *++p; + if (yych == 'o') goto yy56; + goto yy27; +yy12: + yych = *++p; + if (yych == 'u') goto yy52; + goto yy27; +yy13: + yych = *++p; + if (yych == 'e') goto yy45; + goto yy27; +yy14: + ++p; + { token = EQUALS; break; } +yy16: + ++p; + { token = COLON; break; } +yy18: + ++p; + if ((yych = *p) == '|') goto yy43; + { token = PIPE; break; } +yy20: + yych = *++p; + if (yych == 'n') goto yy36; + goto yy27; +yy21: + yych = *++p; + if (yych == 'u') goto yy28; + goto yy27; +yy22: + yych = *++p; + goto yy27; +yy23: + ++p; + { token = TEOF; break; } +yy25: + yych = *++p; + goto yy5; +yy26: + ++p; + yych = *p; +yy27: + if (yybm[0+yych] & 32) { + goto yy26; + } + goto yy10; +yy28: + yych = *++p; + if (yych != 'b') goto yy27; + yych = *++p; + if (yych != 'n') goto yy27; + yych = *++p; + if (yych != 'i') goto yy27; + yych = *++p; + if (yych != 'n') goto yy27; + yych = *++p; + if (yych != 'j') goto yy27; + yych = *++p; + if (yych != 'a') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = SUBNINJA; break; } +yy36: + yych = *++p; + if (yych != 'c') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'u') goto yy27; + yych = *++p; + if (yych != 'd') goto yy27; + yych = *++p; + if (yych != 'e') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = INCLUDE; break; } +yy43: + ++p; + { token = PIPE2; break; } +yy45: + yych = *++p; + if (yych != 'f') goto yy27; + yych = *++p; + if (yych != 'a') goto yy27; + yych = *++p; + if (yych != 'u') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 't') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = DEFAULT; break; } +yy52: + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'e') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = RULE; break; } +yy56: + yych = *++p; + if (yych != 'o') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = POOL; break; } +yy60: + yych = *++p; + if (yych != 'i') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'd') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = BUILD; break; } +yy65: + ++p; + { token = NEWLINE; break; } +yy67: + ++p; + yych = *p; +yy68: + if (yybm[0+yych] & 64) { + goto yy67; + } + if (yych >= 0x01) goto yy70; +yy69: + p = q; + if (yyaccept <= 0) { + goto yy3; + } else { + goto yy5; + } +yy70: + ++p; + { continue; } +yy72: + yyaccept = 0; + q = ++p; + yych = *p; +yy73: + if (yybm[0+yych] & 128) { + goto yy72; + } + if (yych <= '\f') { + if (yych != '\n') goto yy3; + } else { + if (yych <= '\r') goto yy75; + if (yych == '#') goto yy67; + goto yy3; + } + yych = *++p; + goto yy8; +yy75: + ++p; + if ((yych = *p) == '\n') goto yy65; + goto yy69; +} + + } + + last_token_ = start; + ofs_ = p; + if (token != NEWLINE && token != TEOF) + EatWhitespace(); + return token; +} + +bool Lexer::PeekToken(Token token) { + Token t = ReadToken(); + if (t == token) + return true; + UnreadToken(); + return false; +} + +void Lexer::EatWhitespace() { + const char* p = ofs_; + const char* q; + for (;;) { + ofs_ = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 128, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + yych = *p; + if (yych <= ' ') { + if (yych <= 0x00) goto yy82; + if (yych <= 0x1F) goto yy84; + } else { + if (yych == '$') goto yy80; + goto yy84; + } + ++p; + yych = *p; + goto yy92; +yy79: + { continue; } +yy80: + yych = *(q = ++p); + if (yych == '\n') goto yy85; + if (yych == '\r') goto yy87; +yy81: + { break; } +yy82: + ++p; + { break; } +yy84: + yych = *++p; + goto yy81; +yy85: + ++p; + { continue; } +yy87: + yych = *++p; + if (yych == '\n') goto yy89; + p = q; + goto yy81; +yy89: + ++p; + { continue; } +yy91: + ++p; + yych = *p; +yy92: + if (yybm[0+yych] & 128) { + goto yy91; + } + goto yy79; +} + + } +} + +bool Lexer::ReadIdent(string* out) { + const char* p = ofs_; + for (;;) { + const char* start = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 128, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 0, 0, 0, 0, 0, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 128, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + yych = *p; + if (yych <= '@') { + if (yych <= '.') { + if (yych <= ',') goto yy97; + } else { + if (yych <= '/') goto yy97; + if (yych >= ':') goto yy97; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy95; + if (yych <= '^') goto yy97; + } else { + if (yych <= '`') goto yy97; + if (yych >= '{') goto yy97; + } + } +yy95: + ++p; + yych = *p; + goto yy100; +yy96: + { + out->assign(start, p - start); + break; + } +yy97: + ++p; + { return false; } +yy99: + ++p; + yych = *p; +yy100: + if (yybm[0+yych] & 128) { + goto yy99; + } + goto yy96; +} + + } + ofs_ = p; + EatWhitespace(); + return true; +} + +bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { + const char* p = ofs_; + const char* q; + const char* start; + for (;;) { + start = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 128, 128, 0, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 16, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 224, 160, 128, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 0, 128, 128, 128, 128, 128, + 128, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 128, 128, 128, 128, 224, + 128, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + }; + yych = *p; + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy110; + if (yych >= '\n') goto yy107; + } else { + if (yych == '\r') goto yy105; + if (yych >= ' ') goto yy107; + } + } else { + if (yych <= '9') { + if (yych == '$') goto yy109; + } else { + if (yych <= ':') goto yy107; + if (yych == '|') goto yy107; + } + } + ++p; + yych = *p; + goto yy140; +yy104: + { + eval->AddText(StringPiece(start, p - start)); + continue; + } +yy105: + ++p; + if ((yych = *p) == '\n') goto yy137; + { + last_token_ = start; + return Error(DescribeLastError(), err); + } +yy107: + ++p; + { + if (path) { + p = start; + break; + } else { + if (*start == '\n') + break; + eval->AddText(StringPiece(start, 1)); + continue; + } + } +yy109: + yych = *++p; + if (yych <= '-') { + if (yych <= 0x1F) { + if (yych <= '\n') { + if (yych <= '\t') goto yy112; + goto yy124; + } else { + if (yych == '\r') goto yy114; + goto yy112; + } + } else { + if (yych <= '#') { + if (yych <= ' ') goto yy115; + goto yy112; + } else { + if (yych <= '$') goto yy117; + if (yych <= ',') goto yy112; + goto yy119; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '/') goto yy112; + goto yy119; + } else { + if (yych <= ':') goto yy121; + if (yych <= '@') goto yy112; + goto yy119; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy119; + goto yy112; + } else { + if (yych <= 'z') goto yy119; + if (yych <= '{') goto yy123; + goto yy112; + } + } + } +yy110: + ++p; + { + last_token_ = start; + return Error("unexpected EOF", err); + } +yy112: + ++p; +yy113: + { + last_token_ = start; + return Error("bad $-escape (literal $ must be written as $$)", err); + } +yy114: + yych = *++p; + if (yych == '\n') goto yy134; + goto yy113; +yy115: + ++p; + { + eval->AddText(StringPiece(" ", 1)); + continue; + } +yy117: + ++p; + { + eval->AddText(StringPiece("$", 1)); + continue; + } +yy119: + ++p; + yych = *p; + goto yy133; +yy120: + { + eval->AddSpecial(StringPiece(start + 1, p - start - 1)); + continue; + } +yy121: + ++p; + { + eval->AddText(StringPiece(":", 1)); + continue; + } +yy123: + yych = *(q = ++p); + if (yybm[0+yych] & 32) { + goto yy127; + } + goto yy113; +yy124: + ++p; + yych = *p; + if (yybm[0+yych] & 16) { + goto yy124; + } + { + continue; + } +yy127: + ++p; + yych = *p; + if (yybm[0+yych] & 32) { + goto yy127; + } + if (yych == '}') goto yy130; + p = q; + goto yy113; +yy130: + ++p; + { + eval->AddSpecial(StringPiece(start + 2, p - start - 3)); + continue; + } +yy132: + ++p; + yych = *p; +yy133: + if (yybm[0+yych] & 64) { + goto yy132; + } + goto yy120; +yy134: + ++p; + yych = *p; + if (yych == ' ') goto yy134; + { + continue; + } +yy137: + ++p; + { + if (path) + p = start; + break; + } +yy139: + ++p; + yych = *p; +yy140: + if (yybm[0+yych] & 128) { + goto yy139; + } + goto yy104; +} + + } + last_token_ = start; + ofs_ = p; + if (path) + EatWhitespace(); + // Non-path strings end in newlines, so there's no whitespace to eat. + return true; +} diff --git a/src/3rdparty/ninja/src/lexer.h b/src/3rdparty/ninja/src/lexer.h new file mode 100644 index 00000000000..f366556afc5 --- /dev/null +++ b/src/3rdparty/ninja/src/lexer.h @@ -0,0 +1,105 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_LEXER_H_ +#define NINJA_LEXER_H_ + +#include "string_piece.h" + +// Windows may #define ERROR. +#ifdef ERROR +#undef ERROR +#endif + +struct EvalString; + +struct Lexer { + Lexer() {} + /// Helper ctor useful for tests. + explicit Lexer(const char* input); + + enum Token { + ERROR, + BUILD, + COLON, + DEFAULT, + EQUALS, + IDENT, + INCLUDE, + INDENT, + NEWLINE, + PIPE, + PIPE2, + POOL, + RULE, + SUBNINJA, + TEOF, + }; + + /// Return a human-readable form of a token, used in error messages. + static const char* TokenName(Token t); + + /// Return a human-readable token hint, used in error messages. + static const char* TokenErrorHint(Token expected); + + /// If the last token read was an ERROR token, provide more info + /// or the empty string. + string DescribeLastError(); + + /// Start parsing some input. + void Start(StringPiece filename, StringPiece input); + + /// Read a Token from the Token enum. + Token ReadToken(); + + /// Rewind to the last read Token. + void UnreadToken(); + + /// If the next token is \a token, read it and return true. + bool PeekToken(Token token); + + /// Read a simple identifier (a rule or variable name). + /// Returns false if a name can't be read. + bool ReadIdent(string* out); + + /// Read a path (complete with $escapes). + /// Returns false only on error, returned path may be empty if a delimiter + /// (space, newline) is hit. + bool ReadPath(EvalString* path, string* err) { + return ReadEvalString(path, true, err); + } + + /// Read the value side of a var = value line (complete with $escapes). + /// Returns false only on error. + bool ReadVarValue(EvalString* value, string* err) { + return ReadEvalString(value, false, err); + } + + /// Construct an error message with context. + bool Error(const string& message, string* err); + +private: + /// Skip past whitespace (called after each read token/ident/etc.). + void EatWhitespace(); + + /// Read a $-escaped string. + bool ReadEvalString(EvalString* eval, bool path, string* err); + + StringPiece filename_; + StringPiece input_; + const char* ofs_; + const char* last_token_; +}; + +#endif // NINJA_LEXER_H_ diff --git a/src/3rdparty/ninja/src/lexer.in.cc b/src/3rdparty/ninja/src/lexer.in.cc new file mode 100644 index 00000000000..f8612390895 --- /dev/null +++ b/src/3rdparty/ninja/src/lexer.in.cc @@ -0,0 +1,273 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lexer.h" + +#include + +#include "eval_env.h" +#include "util.h" + +bool Lexer::Error(const string& message, string* err) { + // Compute line/column. + int line = 1; + const char* context = input_.str_; + for (const char* p = input_.str_; p < last_token_; ++p) { + if (*p == '\n') { + ++line; + context = p + 1; + } + } + int col = last_token_ ? (int)(last_token_ - context) : 0; + + char buf[1024]; + snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); + *err = buf; + *err += message + "\n"; + + // Add some context to the message. + const int kTruncateColumn = 72; + if (col > 0 && col < kTruncateColumn) { + int len; + bool truncated = true; + for (len = 0; len < kTruncateColumn; ++len) { + if (context[len] == 0 || context[len] == '\n') { + truncated = false; + break; + } + } + *err += string(context, len); + if (truncated) + *err += "..."; + *err += "\n"; + *err += string(col, ' '); + *err += "^ near here"; + } + + return false; +} + +Lexer::Lexer(const char* input) { + Start("input", input); +} + +void Lexer::Start(StringPiece filename, StringPiece input) { + filename_ = filename; + input_ = input; + ofs_ = input_.str_; + last_token_ = NULL; +} + +const char* Lexer::TokenName(Token t) { + switch (t) { + case ERROR: return "lexing error"; + case BUILD: return "'build'"; + case COLON: return "':'"; + case DEFAULT: return "'default'"; + case EQUALS: return "'='"; + case IDENT: return "identifier"; + case INCLUDE: return "'include'"; + case INDENT: return "indent"; + case NEWLINE: return "newline"; + case PIPE2: return "'||'"; + case PIPE: return "'|'"; + case POOL: return "'pool'"; + case RULE: return "'rule'"; + case SUBNINJA: return "'subninja'"; + case TEOF: return "eof"; + } + return NULL; // not reached +} + +const char* Lexer::TokenErrorHint(Token expected) { + switch (expected) { + case COLON: + return " ($ also escapes ':')"; + default: + return ""; + } +} + +string Lexer::DescribeLastError() { + if (last_token_) { + switch (last_token_[0]) { + case '\t': + return "tabs are not allowed, use spaces"; + } + } + return "lexing error"; +} + +void Lexer::UnreadToken() { + ofs_ = last_token_; +} + +Lexer::Token Lexer::ReadToken() { + const char* p = ofs_; + const char* q; + const char* start; + Lexer::Token token; + for (;;) { + start = p; + /*!re2c + re2c:define:YYCTYPE = "unsigned char"; + re2c:define:YYCURSOR = p; + re2c:define:YYMARKER = q; + re2c:yyfill:enable = 0; + + nul = "\000"; + simple_varname = [a-zA-Z0-9_-]+; + varname = [a-zA-Z0-9_.-]+; + + [ ]*"#"[^\000\n]*"\n" { continue; } + [ ]*"\r\n" { token = NEWLINE; break; } + [ ]*"\n" { token = NEWLINE; break; } + [ ]+ { token = INDENT; break; } + "build" { token = BUILD; break; } + "pool" { token = POOL; break; } + "rule" { token = RULE; break; } + "default" { token = DEFAULT; break; } + "=" { token = EQUALS; break; } + ":" { token = COLON; break; } + "||" { token = PIPE2; break; } + "|" { token = PIPE; break; } + "include" { token = INCLUDE; break; } + "subninja" { token = SUBNINJA; break; } + varname { token = IDENT; break; } + nul { token = TEOF; break; } + [^] { token = ERROR; break; } + */ + } + + last_token_ = start; + ofs_ = p; + if (token != NEWLINE && token != TEOF) + EatWhitespace(); + return token; +} + +bool Lexer::PeekToken(Token token) { + Token t = ReadToken(); + if (t == token) + return true; + UnreadToken(); + return false; +} + +void Lexer::EatWhitespace() { + const char* p = ofs_; + const char* q; + for (;;) { + ofs_ = p; + /*!re2c + [ ]+ { continue; } + "$\r\n" { continue; } + "$\n" { continue; } + nul { break; } + [^] { break; } + */ + } +} + +bool Lexer::ReadIdent(string* out) { + const char* p = ofs_; + for (;;) { + const char* start = p; + /*!re2c + varname { + out->assign(start, p - start); + break; + } + [^] { return false; } + */ + } + ofs_ = p; + EatWhitespace(); + return true; +} + +bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { + const char* p = ofs_; + const char* q; + const char* start; + for (;;) { + start = p; + /*!re2c + [^$ :\r\n|\000]+ { + eval->AddText(StringPiece(start, p - start)); + continue; + } + "\r\n" { + if (path) + p = start; + break; + } + [ :|\n] { + if (path) { + p = start; + break; + } else { + if (*start == '\n') + break; + eval->AddText(StringPiece(start, 1)); + continue; + } + } + "$$" { + eval->AddText(StringPiece("$", 1)); + continue; + } + "$ " { + eval->AddText(StringPiece(" ", 1)); + continue; + } + "$\r\n"[ ]* { + continue; + } + "$\n"[ ]* { + continue; + } + "${"varname"}" { + eval->AddSpecial(StringPiece(start + 2, p - start - 3)); + continue; + } + "$"simple_varname { + eval->AddSpecial(StringPiece(start + 1, p - start - 1)); + continue; + } + "$:" { + eval->AddText(StringPiece(":", 1)); + continue; + } + "$". { + last_token_ = start; + return Error("bad $-escape (literal $ must be written as $$)", err); + } + nul { + last_token_ = start; + return Error("unexpected EOF", err); + } + [^] { + last_token_ = start; + return Error(DescribeLastError(), err); + } + */ + } + last_token_ = start; + ofs_ = p; + if (path) + EatWhitespace(); + // Non-path strings end in newlines, so there's no whitespace to eat. + return true; +} diff --git a/src/3rdparty/ninja/src/lexer_test.cc b/src/3rdparty/ninja/src/lexer_test.cc new file mode 100644 index 00000000000..331d8e1ea91 --- /dev/null +++ b/src/3rdparty/ninja/src/lexer_test.cc @@ -0,0 +1,96 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lexer.h" + +#include "eval_env.h" +#include "test.h" + +TEST(Lexer, ReadVarValue) { + Lexer lexer("plain text $var $VaR ${x}\n"); + EvalString eval; + string err; + EXPECT_TRUE(lexer.ReadVarValue(&eval, &err)); + EXPECT_EQ("", err); + EXPECT_EQ("[plain text ][$var][ ][$VaR][ ][$x]", + eval.Serialize()); +} + +TEST(Lexer, ReadEvalStringEscapes) { + Lexer lexer("$ $$ab c$: $\ncde\n"); + EvalString eval; + string err; + EXPECT_TRUE(lexer.ReadVarValue(&eval, &err)); + EXPECT_EQ("", err); + EXPECT_EQ("[ $ab c: cde]", + eval.Serialize()); +} + +TEST(Lexer, ReadIdent) { + Lexer lexer("foo baR baz_123 foo-bar"); + string ident; + EXPECT_TRUE(lexer.ReadIdent(&ident)); + EXPECT_EQ("foo", ident); + EXPECT_TRUE(lexer.ReadIdent(&ident)); + EXPECT_EQ("baR", ident); + EXPECT_TRUE(lexer.ReadIdent(&ident)); + EXPECT_EQ("baz_123", ident); + EXPECT_TRUE(lexer.ReadIdent(&ident)); + EXPECT_EQ("foo-bar", ident); +} + +TEST(Lexer, ReadIdentCurlies) { + // Verify that ReadIdent includes dots in the name, + // but in an expansion $bar.dots stops at the dot. + Lexer lexer("foo.dots $bar.dots ${bar.dots}\n"); + string ident; + EXPECT_TRUE(lexer.ReadIdent(&ident)); + EXPECT_EQ("foo.dots", ident); + + EvalString eval; + string err; + EXPECT_TRUE(lexer.ReadVarValue(&eval, &err)); + EXPECT_EQ("", err); + EXPECT_EQ("[$bar][.dots ][$bar.dots]", + eval.Serialize()); +} + +TEST(Lexer, Error) { + Lexer lexer("foo$\nbad $"); + EvalString eval; + string err; + ASSERT_FALSE(lexer.ReadVarValue(&eval, &err)); + EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" + "bad $\n" + " ^ near here" + , err); +} + +TEST(Lexer, CommentEOF) { + // Verify we don't run off the end of the string when the EOF is + // mid-comment. + Lexer lexer("# foo"); + Lexer::Token token = lexer.ReadToken(); + EXPECT_EQ(Lexer::ERROR, token); +} + +TEST(Lexer, Tabs) { + // Verify we print a useful error on a disallowed character. + Lexer lexer(" \tfoobar"); + Lexer::Token token = lexer.ReadToken(); + EXPECT_EQ(Lexer::INDENT, token); + token = lexer.ReadToken(); + EXPECT_EQ(Lexer::ERROR, token); + EXPECT_EQ("tabs are not allowed, use spaces", lexer.DescribeLastError()); +} diff --git a/src/3rdparty/ninja/src/line_printer.cc b/src/3rdparty/ninja/src/line_printer.cc new file mode 100644 index 00000000000..2cd3e174c49 --- /dev/null +++ b/src/3rdparty/ninja/src/line_printer.cc @@ -0,0 +1,141 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "line_printer.h" + +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#endif + +#include "util.h" + +LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) { +#ifndef _WIN32 + const char* term = getenv("TERM"); + smart_terminal_ = isatty(1) && term && string(term) != "dumb"; +#else + // Disable output buffer. It'd be nice to use line buffering but + // MSDN says: "For some systems, [_IOLBF] provides line + // buffering. However, for Win32, the behavior is the same as _IOFBF + // - Full Buffering." + setvbuf(stdout, NULL, _IONBF, 0); + console_ = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); +#endif +} + +void LinePrinter::Print(string to_print, LineType type) { + if (console_locked_) { + line_buffer_ = to_print; + line_type_ = type; + return; + } + + if (smart_terminal_) { + printf("\r"); // Print over previous line, if any. + // On Windows, calling a C library function writing to stdout also handles + // pausing the executable when the "Pause" key or Ctrl-S is pressed. + } + + if (smart_terminal_ && type == ELIDE) { +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(console_, &csbi); + + to_print = ElideMiddle(to_print, static_cast(csbi.dwSize.X)); + // We don't want to have the cursor spamming back and forth, so instead of + // printf use WriteConsoleOutput which updates the contents of the buffer, + // but doesn't move the cursor position. + COORD buf_size = { csbi.dwSize.X, 1 }; + COORD zero_zero = { 0, 0 }; + SMALL_RECT target = { + csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, + static_cast(csbi.dwCursorPosition.X + csbi.dwSize.X - 1), + csbi.dwCursorPosition.Y + }; + vector char_data(csbi.dwSize.X); + for (size_t i = 0; i < static_cast(csbi.dwSize.X); ++i) { + char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' '; + char_data[i].Attributes = csbi.wAttributes; + } + WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target); +#else + // Limit output to width of the terminal if provided so we don't cause + // line-wrapping. + winsize size; + if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) { + to_print = ElideMiddle(to_print, size.ws_col); + } + printf("%s", to_print.c_str()); + printf("\x1B[K"); // Clear to end of line. + fflush(stdout); +#endif + + have_blank_line_ = false; + } else { + printf("%s\n", to_print.c_str()); + } +} + +void LinePrinter::PrintOrBuffer(const char* data, size_t size) { + if (console_locked_) { + output_buffer_.append(data, size); + } else { + // Avoid printf and C strings, since the actual output might contain null + // bytes like UTF-16 does (yuck). + fwrite(data, 1, size, stdout); + } +} + +void LinePrinter::PrintOnNewLine(const string& to_print) { + if (console_locked_ && !line_buffer_.empty()) { + output_buffer_.append(line_buffer_); + output_buffer_.append(1, '\n'); + line_buffer_.clear(); + } + if (!have_blank_line_) { + PrintOrBuffer("\n", 1); + } + if (!to_print.empty()) { + PrintOrBuffer(&to_print[0], to_print.size()); + } + have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n'; +} + +void LinePrinter::SetConsoleLocked(bool locked) { + if (locked == console_locked_) + return; + + if (locked) + PrintOnNewLine(""); + + console_locked_ = locked; + + if (!locked) { + PrintOnNewLine(output_buffer_); + if (!line_buffer_.empty()) { + Print(line_buffer_, line_type_); + } + output_buffer_.clear(); + line_buffer_.clear(); + } +} diff --git a/src/3rdparty/ninja/src/line_printer.h b/src/3rdparty/ninja/src/line_printer.h new file mode 100644 index 00000000000..55225e52117 --- /dev/null +++ b/src/3rdparty/ninja/src/line_printer.h @@ -0,0 +1,72 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_LINE_PRINTER_H_ +#define NINJA_LINE_PRINTER_H_ + +#include +#include +using namespace std; + +/// Prints lines of text, possibly overprinting previously printed lines +/// if the terminal supports it. +struct LinePrinter { + LinePrinter(); + + bool is_smart_terminal() const { return smart_terminal_; } + void set_smart_terminal(bool smart) { smart_terminal_ = smart; } + + enum LineType { + FULL, + ELIDE + }; + /// Overprints the current line. If type is ELIDE, elides to_print to fit on + /// one line. + void Print(string to_print, LineType type); + + /// Prints a string on a new line, not overprinting previous output. + void PrintOnNewLine(const string& to_print); + + /// Lock or unlock the console. Any output sent to the LinePrinter while the + /// console is locked will not be printed until it is unlocked. + void SetConsoleLocked(bool locked); + + private: + /// Whether we can do fancy terminal control codes. + bool smart_terminal_; + + /// Whether the caret is at the beginning of a blank line. + bool have_blank_line_; + + /// Whether console is locked. + bool console_locked_; + + /// Buffered current line while console is locked. + string line_buffer_; + + /// Buffered line type while console is locked. + LineType line_type_; + + /// Buffered console output while console is locked. + string output_buffer_; + +#ifdef _WIN32 + void* console_; +#endif + + /// Print the given data to the console, or buffer it if it is locked. + void PrintOrBuffer(const char *data, size_t size); +}; + +#endif // NINJA_LINE_PRINTER_H_ diff --git a/src/3rdparty/ninja/src/manifest_parser.cc b/src/3rdparty/ninja/src/manifest_parser.cc new file mode 100644 index 00000000000..27c423bbb73 --- /dev/null +++ b/src/3rdparty/ninja/src/manifest_parser.cc @@ -0,0 +1,447 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "manifest_parser.h" + +#include +#include +#include + +#include "disk_interface.h" +#include "graph.h" +#include "metrics.h" +#include "state.h" +#include "util.h" +#include "version.h" + +ManifestParser::ManifestParser(State* state, FileReader* file_reader, + ManifestParserOptions options) + : state_(state), file_reader_(file_reader), + options_(options), quiet_(false) { + env_ = &state->bindings_; +} + +bool ManifestParser::Load(const string& filename, string* err, Lexer* parent) { + METRIC_RECORD(".ninja parse"); + string contents; + string read_err; + if (file_reader_->ReadFile(filename, &contents, &read_err) != FileReader::Okay) { + *err = "loading '" + filename + "': " + read_err; + if (parent) + parent->Error(string(*err), err); + return false; + } + + // The lexer needs a nul byte at the end of its input, to know when it's done. + // It takes a StringPiece, and StringPiece's string constructor uses + // string::data(). data()'s return value isn't guaranteed to be + // null-terminated (although in practice - libc++, libstdc++, msvc's stl -- + // it is, and C++11 demands that too), so add an explicit nul byte. + contents.resize(contents.size() + 1); + + return Parse(filename, contents, err); +} + +bool ManifestParser::Parse(const string& filename, const string& input, + string* err) { + lexer_.Start(filename, input); + + for (;;) { + Lexer::Token token = lexer_.ReadToken(); + switch (token) { + case Lexer::POOL: + if (!ParsePool(err)) + return false; + break; + case Lexer::BUILD: + if (!ParseEdge(err)) + return false; + break; + case Lexer::RULE: + if (!ParseRule(err)) + return false; + break; + case Lexer::DEFAULT: + if (!ParseDefault(err)) + return false; + break; + case Lexer::IDENT: { + lexer_.UnreadToken(); + string name; + EvalString let_value; + if (!ParseLet(&name, &let_value, err)) + return false; + string value = let_value.Evaluate(env_); + // Check ninja_required_version immediately so we can exit + // before encountering any syntactic surprises. + if (name == "ninja_required_version") + CheckNinjaVersion(value); + env_->AddBinding(name, value); + break; + } + case Lexer::INCLUDE: + if (!ParseFileInclude(false, err)) + return false; + break; + case Lexer::SUBNINJA: + if (!ParseFileInclude(true, err)) + return false; + break; + case Lexer::ERROR: { + return lexer_.Error(lexer_.DescribeLastError(), err); + } + case Lexer::TEOF: + return true; + case Lexer::NEWLINE: + break; + default: + return lexer_.Error(string("unexpected ") + Lexer::TokenName(token), + err); + } + } + return false; // not reached +} + + +bool ManifestParser::ParsePool(string* err) { + string name; + if (!lexer_.ReadIdent(&name)) + return lexer_.Error("expected pool name", err); + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + if (state_->LookupPool(name) != NULL) + return lexer_.Error("duplicate pool '" + name + "'", err); + + int depth = -1; + + while (lexer_.PeekToken(Lexer::INDENT)) { + string key; + EvalString value; + if (!ParseLet(&key, &value, err)) + return false; + + if (key == "depth") { + string depth_string = value.Evaluate(env_); + depth = atol(depth_string.c_str()); + if (depth < 0) + return lexer_.Error("invalid pool depth", err); + } else { + return lexer_.Error("unexpected variable '" + key + "'", err); + } + } + + if (depth < 0) + return lexer_.Error("expected 'depth =' line", err); + + state_->AddPool(new Pool(name, depth)); + return true; +} + + +bool ManifestParser::ParseRule(string* err) { + string name; + if (!lexer_.ReadIdent(&name)) + return lexer_.Error("expected rule name", err); + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + if (env_->LookupRuleCurrentScope(name) != NULL) + return lexer_.Error("duplicate rule '" + name + "'", err); + + Rule* rule = new Rule(name); // XXX scoped_ptr + + while (lexer_.PeekToken(Lexer::INDENT)) { + string key; + EvalString value; + if (!ParseLet(&key, &value, err)) + return false; + + if (Rule::IsReservedBinding(key)) { + rule->AddBinding(key, value); + } else { + // Die on other keyvals for now; revisit if we want to add a + // scope here. + return lexer_.Error("unexpected variable '" + key + "'", err); + } + } + + if (rule->bindings_["rspfile"].empty() != + rule->bindings_["rspfile_content"].empty()) { + return lexer_.Error("rspfile and rspfile_content need to be " + "both specified", err); + } + + if (rule->bindings_["command"].empty()) + return lexer_.Error("expected 'command =' line", err); + + env_->AddRule(rule); + return true; +} + +bool ManifestParser::ParseLet(string* key, EvalString* value, string* err) { + if (!lexer_.ReadIdent(key)) + return lexer_.Error("expected variable name", err); + if (!ExpectToken(Lexer::EQUALS, err)) + return false; + if (!lexer_.ReadVarValue(value, err)) + return false; + return true; +} + +bool ManifestParser::ParseDefault(string* err) { + EvalString eval; + if (!lexer_.ReadPath(&eval, err)) + return false; + if (eval.empty()) + return lexer_.Error("expected target name", err); + + do { + string path = eval.Evaluate(env_); + string path_err; + uint64_t slash_bits; // Unused because this only does lookup. + if (!CanonicalizePath(&path, &slash_bits, &path_err)) + return lexer_.Error(path_err, err); + if (!state_->AddDefault(path, &path_err)) + return lexer_.Error(path_err, err); + + eval.Clear(); + if (!lexer_.ReadPath(&eval, err)) + return false; + } while (!eval.empty()); + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + return true; +} + +bool ManifestParser::ParseEdge(string* err) { + vector ins, outs; + + { + EvalString out; + if (!lexer_.ReadPath(&out, err)) + return false; + while (!out.empty()) { + outs.push_back(out); + + out.Clear(); + if (!lexer_.ReadPath(&out, err)) + return false; + } + } + + // Add all implicit outs, counting how many as we go. + int implicit_outs = 0; + if (lexer_.PeekToken(Lexer::PIPE)) { + for (;;) { + EvalString out; + if (!lexer_.ReadPath(&out, err)) + return err; + if (out.empty()) + break; + outs.push_back(out); + ++implicit_outs; + } + } + + if (outs.empty()) + return lexer_.Error("expected path", err); + + if (!ExpectToken(Lexer::COLON, err)) + return false; + + string rule_name; + if (!lexer_.ReadIdent(&rule_name)) + return lexer_.Error("expected build command name", err); + + const Rule* rule = env_->LookupRule(rule_name); + if (!rule) + return lexer_.Error("unknown build rule '" + rule_name + "'", err); + + for (;;) { + // XXX should we require one path here? + EvalString in; + if (!lexer_.ReadPath(&in, err)) + return false; + if (in.empty()) + break; + ins.push_back(in); + } + + // Add all implicit deps, counting how many as we go. + int implicit = 0; + if (lexer_.PeekToken(Lexer::PIPE)) { + for (;;) { + EvalString in; + if (!lexer_.ReadPath(&in, err)) + return err; + if (in.empty()) + break; + ins.push_back(in); + ++implicit; + } + } + + // Add all order-only deps, counting how many as we go. + int order_only = 0; + if (lexer_.PeekToken(Lexer::PIPE2)) { + for (;;) { + EvalString in; + if (!lexer_.ReadPath(&in, err)) + return false; + if (in.empty()) + break; + ins.push_back(in); + ++order_only; + } + } + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + // Bindings on edges are rare, so allocate per-edge envs only when needed. + bool has_indent_token = lexer_.PeekToken(Lexer::INDENT); + BindingEnv* env = has_indent_token ? new BindingEnv(env_) : env_; + while (has_indent_token) { + string key; + EvalString val; + if (!ParseLet(&key, &val, err)) + return false; + + env->AddBinding(key, val.Evaluate(env_)); + has_indent_token = lexer_.PeekToken(Lexer::INDENT); + } + + Edge* edge = state_->AddEdge(rule); + edge->env_ = env; + + string pool_name = edge->GetBinding("pool"); + if (!pool_name.empty()) { + Pool* pool = state_->LookupPool(pool_name); + if (pool == NULL) + return lexer_.Error("unknown pool name '" + pool_name + "'", err); + edge->pool_ = pool; + } + + edge->outputs_.reserve(outs.size()); + for (size_t i = 0, e = outs.size(); i != e; ++i) { + string path = outs[i].Evaluate(env); + string path_err; + uint64_t slash_bits; + if (!CanonicalizePath(&path, &slash_bits, &path_err)) + return lexer_.Error(path_err, err); + if (!state_->AddOut(edge, path, slash_bits)) { + if (options_.dupe_edge_action_ == kDupeEdgeActionError) { + lexer_.Error("multiple rules generate " + path + " [-w dupbuild=err]", + err); + return false; + } else { + if (!quiet_) { + Warning("multiple rules generate %s. " + "builds involving this target will not be correct; " + "continuing anyway [-w dupbuild=warn]", + path.c_str()); + } + if (e - i <= static_cast(implicit_outs)) + --implicit_outs; + } + } + } + if (edge->outputs_.empty()) { + // All outputs of the edge are already created by other edges. Don't add + // this edge. Do this check before input nodes are connected to the edge. + state_->edges_.pop_back(); + delete edge; + return true; + } + edge->implicit_outs_ = implicit_outs; + + edge->inputs_.reserve(ins.size()); + for (vector::iterator i = ins.begin(); i != ins.end(); ++i) { + string path = i->Evaluate(env); + string path_err; + uint64_t slash_bits; + if (!CanonicalizePath(&path, &slash_bits, &path_err)) + return lexer_.Error(path_err, err); + state_->AddIn(edge, path, slash_bits); + } + edge->implicit_deps_ = implicit; + edge->order_only_deps_ = order_only; + + if (options_.phony_cycle_action_ == kPhonyCycleActionWarn && + edge->maybe_phonycycle_diagnostic()) { + // CMake 2.8.12.x and 3.0.x incorrectly write phony build statements + // that reference themselves. Ninja used to tolerate these in the + // build graph but that has since been fixed. Filter them out to + // support users of those old CMake versions. + Node* out = edge->outputs_[0]; + vector::iterator new_end = + remove(edge->inputs_.begin(), edge->inputs_.end(), out); + if (new_end != edge->inputs_.end()) { + edge->inputs_.erase(new_end, edge->inputs_.end()); + if (!quiet_) { + Warning("phony target '%s' names itself as an input; " + "ignoring [-w phonycycle=warn]", + out->path().c_str()); + } + } + } + + // Multiple outputs aren't (yet?) supported with depslog. + string deps_type = edge->GetBinding("deps"); + if (!deps_type.empty() && edge->outputs_.size() > 1) { + return lexer_.Error("multiple outputs aren't (yet?) supported by depslog; " + "bring this up on the mailing list if it affects you", + err); + } + + return true; +} + +bool ManifestParser::ParseFileInclude(bool new_scope, string* err) { + EvalString eval; + if (!lexer_.ReadPath(&eval, err)) + return false; + string path = eval.Evaluate(env_); + + ManifestParser subparser(state_, file_reader_, options_); + if (new_scope) { + subparser.env_ = new BindingEnv(env_); + } else { + subparser.env_ = env_; + } + + if (!subparser.Load(path, err, &lexer_)) + return false; + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + return true; +} + +bool ManifestParser::ExpectToken(Lexer::Token expected, string* err) { + Lexer::Token token = lexer_.ReadToken(); + if (token != expected) { + string message = string("expected ") + Lexer::TokenName(expected); + message += string(", got ") + Lexer::TokenName(token); + message += Lexer::TokenErrorHint(expected); + return lexer_.Error(message, err); + } + return true; +} diff --git a/src/3rdparty/ninja/src/manifest_parser.h b/src/3rdparty/ninja/src/manifest_parser.h new file mode 100644 index 00000000000..2136018a99f --- /dev/null +++ b/src/3rdparty/ninja/src/manifest_parser.h @@ -0,0 +1,87 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_MANIFEST_PARSER_H_ +#define NINJA_MANIFEST_PARSER_H_ + +#include + +using namespace std; + +#include "lexer.h" + +struct BindingEnv; +struct EvalString; +struct FileReader; +struct State; + +enum DupeEdgeAction { + kDupeEdgeActionWarn, + kDupeEdgeActionError, +}; + +enum PhonyCycleAction { + kPhonyCycleActionWarn, + kPhonyCycleActionError, +}; + +struct ManifestParserOptions { + ManifestParserOptions() + : dupe_edge_action_(kDupeEdgeActionWarn), + phony_cycle_action_(kPhonyCycleActionWarn) {} + DupeEdgeAction dupe_edge_action_; + PhonyCycleAction phony_cycle_action_; +}; + +/// Parses .ninja files. +struct ManifestParser { + ManifestParser(State* state, FileReader* file_reader, + ManifestParserOptions options = ManifestParserOptions()); + + /// Load and parse a file. + bool Load(const string& filename, string* err, Lexer* parent = NULL); + + /// Parse a text string of input. Used by tests. + bool ParseTest(const string& input, string* err) { + quiet_ = true; + return Parse("input", input, err); + } + +private: + /// Parse a file, given its contents as a string. + bool Parse(const string& filename, const string& input, string* err); + + /// Parse various statement types. + bool ParsePool(string* err); + bool ParseRule(string* err); + bool ParseLet(string* key, EvalString* val, string* err); + bool ParseEdge(string* err); + bool ParseDefault(string* err); + + /// Parse either a 'subninja' or 'include' line. + bool ParseFileInclude(bool new_scope, string* err); + + /// If the next token is not \a expected, produce an error string + /// saying "expectd foo, got bar". + bool ExpectToken(Lexer::Token expected, string* err); + + State* state_; + BindingEnv* env_; + FileReader* file_reader_; + Lexer lexer_; + ManifestParserOptions options_; + bool quiet_; +}; + +#endif // NINJA_MANIFEST_PARSER_H_ diff --git a/src/3rdparty/ninja/src/manifest_parser_perftest.cc b/src/3rdparty/ninja/src/manifest_parser_perftest.cc new file mode 100644 index 00000000000..67d11f91664 --- /dev/null +++ b/src/3rdparty/ninja/src/manifest_parser_perftest.cc @@ -0,0 +1,118 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests manifest parser performance. Expects to be run in ninja's root +// directory. + +#include + +#include +#include +#include +#include + +#ifdef _WIN32 +#include "getopt.h" +#include +#else +#include +#include +#endif + +#include "disk_interface.h" +#include "graph.h" +#include "manifest_parser.h" +#include "metrics.h" +#include "state.h" +#include "util.h" + +bool WriteFakeManifests(const string& dir, string* err) { + RealDiskInterface disk_interface; + TimeStamp mtime = disk_interface.Stat(dir + "/build.ninja", err); + if (mtime != 0) // 0 means that the file doesn't exist yet. + return mtime != -1; + + string command = "python misc/write_fake_manifests.py " + dir; + printf("Creating manifest data..."); fflush(stdout); + int exit_code = system(command.c_str()); + printf("done.\n"); + if (exit_code != 0) + *err = "Failed to run " + command; + return exit_code == 0; +} + +int LoadManifests(bool measure_command_evaluation) { + string err; + RealDiskInterface disk_interface; + State state; + ManifestParser parser(&state, &disk_interface); + if (!parser.Load("build.ninja", &err)) { + fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); + exit(1); + } + // Doing an empty build involves reading the manifest and evaluating all + // commands required for the requested targets. So include command + // evaluation in the perftest by default. + int optimization_guard = 0; + if (measure_command_evaluation) + for (size_t i = 0; i < state.edges_.size(); ++i) + optimization_guard += state.edges_[i]->EvaluateCommand().size(); + return optimization_guard; +} + +int main(int argc, char* argv[]) { + bool measure_command_evaluation = true; + int opt; + while ((opt = getopt(argc, argv, const_cast("fh"))) != -1) { + switch (opt) { + case 'f': + measure_command_evaluation = false; + break; + case 'h': + default: + printf("usage: manifest_parser_perftest\n" +"\n" +"options:\n" +" -f only measure manifest load time, not command evaluation time\n" + ); + return 1; + } + } + + const char kManifestDir[] = "build/manifest_perftest"; + + string err; + if (!WriteFakeManifests(kManifestDir, &err)) { + fprintf(stderr, "Failed to write test data: %s\n", err.c_str()); + return 1; + } + + if (chdir(kManifestDir) < 0) + Fatal("chdir: %s", strerror(errno)); + + const int kNumRepetitions = 5; + vector times; + for (int i = 0; i < kNumRepetitions; ++i) { + int64_t start = GetTimeMillis(); + int optimization_guard = LoadManifests(measure_command_evaluation); + int delta = (int)(GetTimeMillis() - start); + printf("%dms (hash: %x)\n", delta, optimization_guard); + times.push_back(delta); + } + + int min = *min_element(times.begin(), times.end()); + int max = *max_element(times.begin(), times.end()); + float total = accumulate(times.begin(), times.end(), 0.0f); + printf("min %dms max %dms avg %.1fms\n", min, max, total / times.size()); +} diff --git a/src/3rdparty/ninja/src/manifest_parser_test.cc b/src/3rdparty/ninja/src/manifest_parser_test.cc new file mode 100644 index 00000000000..39ed810658a --- /dev/null +++ b/src/3rdparty/ninja/src/manifest_parser_test.cc @@ -0,0 +1,1079 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "manifest_parser.h" + +#include +#include + +#include "graph.h" +#include "state.h" +#include "test.h" + +struct ParserTest : public testing::Test { + void AssertParse(const char* input) { + ManifestParser parser(&state, &fs_); + string err; + EXPECT_TRUE(parser.ParseTest(input, &err)); + ASSERT_EQ("", err); + VerifyGraph(state); + } + + State state; + VirtualFileSystem fs_; +}; + +TEST_F(ParserTest, Empty) { + ASSERT_NO_FATAL_FAILURE(AssertParse("")); +} + +TEST_F(ParserTest, Rules) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"\n" +"rule date\n" +" command = date > $out\n" +"\n" +"build result: cat in_1.cc in-2.O\n")); + + ASSERT_EQ(3u, state.bindings_.GetRules().size()); + const Rule* rule = state.bindings_.GetRules().begin()->second; + EXPECT_EQ("cat", rule->name()); + EXPECT_EQ("[cat ][$in][ > ][$out]", + rule->GetBinding("command")->Serialize()); +} + +TEST_F(ParserTest, RuleAttributes) { + // Check that all of the allowed rule attributes are parsed ok. + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = a\n" +" depfile = a\n" +" deps = a\n" +" description = a\n" +" generator = a\n" +" restat = a\n" +" rspfile = a\n" +" rspfile_content = a\n" +)); +} + +TEST_F(ParserTest, IgnoreIndentedComments) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +" #indented comment\n" +"rule cat\n" +" command = cat $in > $out\n" +" #generator = 1\n" +" restat = 1 # comment\n" +" #comment\n" +"build result: cat in_1.cc in-2.O\n" +" #comment\n")); + + ASSERT_EQ(2u, state.bindings_.GetRules().size()); + const Rule* rule = state.bindings_.GetRules().begin()->second; + EXPECT_EQ("cat", rule->name()); + Edge* edge = state.GetNode("result", 0)->in_edge(); + EXPECT_TRUE(edge->GetBindingBool("restat")); + EXPECT_FALSE(edge->GetBindingBool("generator")); +} + +TEST_F(ParserTest, IgnoreIndentedBlankLines) { + // the indented blanks used to cause parse errors + ASSERT_NO_FATAL_FAILURE(AssertParse( +" \n" +"rule cat\n" +" command = cat $in > $out\n" +" \n" +"build result: cat in_1.cc in-2.O\n" +" \n" +"variable=1\n")); + + // the variable must be in the top level environment + EXPECT_EQ("1", state.bindings_.LookupVariable("variable")); +} + +TEST_F(ParserTest, ResponseFiles) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat_rsp\n" +" command = cat $rspfile > $out\n" +" rspfile = $rspfile\n" +" rspfile_content = $in\n" +"\n" +"build out: cat_rsp in\n" +" rspfile=out.rsp\n")); + + ASSERT_EQ(2u, state.bindings_.GetRules().size()); + const Rule* rule = state.bindings_.GetRules().begin()->second; + EXPECT_EQ("cat_rsp", rule->name()); + EXPECT_EQ("[cat ][$rspfile][ > ][$out]", + rule->GetBinding("command")->Serialize()); + EXPECT_EQ("[$rspfile]", rule->GetBinding("rspfile")->Serialize()); + EXPECT_EQ("[$in]", rule->GetBinding("rspfile_content")->Serialize()); +} + +TEST_F(ParserTest, InNewline) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat_rsp\n" +" command = cat $in_newline > $out\n" +"\n" +"build out: cat_rsp in in2\n" +" rspfile=out.rsp\n")); + + ASSERT_EQ(2u, state.bindings_.GetRules().size()); + const Rule* rule = state.bindings_.GetRules().begin()->second; + EXPECT_EQ("cat_rsp", rule->name()); + EXPECT_EQ("[cat ][$in_newline][ > ][$out]", + rule->GetBinding("command")->Serialize()); + + Edge* edge = state.edges_[0]; + EXPECT_EQ("cat in\nin2 > out", edge->EvaluateCommand()); +} + +TEST_F(ParserTest, Variables) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"l = one-letter-test\n" +"rule link\n" +" command = ld $l $extra $with_under -o $out $in\n" +"\n" +"extra = -pthread\n" +"with_under = -under\n" +"build a: link b c\n" +"nested1 = 1\n" +"nested2 = $nested1/2\n" +"build supernested: link x\n" +" extra = $nested2/3\n")); + + ASSERT_EQ(2u, state.edges_.size()); + Edge* edge = state.edges_[0]; + EXPECT_EQ("ld one-letter-test -pthread -under -o a b c", + edge->EvaluateCommand()); + EXPECT_EQ("1/2", state.bindings_.LookupVariable("nested2")); + + edge = state.edges_[1]; + EXPECT_EQ("ld one-letter-test 1/2/3 -under -o supernested x", + edge->EvaluateCommand()); +} + +TEST_F(ParserTest, VariableScope) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"foo = bar\n" +"rule cmd\n" +" command = cmd $foo $in $out\n" +"\n" +"build inner: cmd a\n" +" foo = baz\n" +"build outer: cmd b\n" +"\n" // Extra newline after build line tickles a regression. +)); + + ASSERT_EQ(2u, state.edges_.size()); + EXPECT_EQ("cmd baz a inner", state.edges_[0]->EvaluateCommand()); + EXPECT_EQ("cmd bar b outer", state.edges_[1]->EvaluateCommand()); +} + +TEST_F(ParserTest, Continuation) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule link\n" +" command = foo bar $\n" +" baz\n" +"\n" +"build a: link c $\n" +" d e f\n")); + + ASSERT_EQ(2u, state.bindings_.GetRules().size()); + const Rule* rule = state.bindings_.GetRules().begin()->second; + EXPECT_EQ("link", rule->name()); + EXPECT_EQ("[foo bar baz]", rule->GetBinding("command")->Serialize()); +} + +TEST_F(ParserTest, Backslash) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"foo = bar\\baz\n" +"foo2 = bar\\ baz\n" +)); + EXPECT_EQ("bar\\baz", state.bindings_.LookupVariable("foo")); + EXPECT_EQ("bar\\ baz", state.bindings_.LookupVariable("foo2")); +} + +TEST_F(ParserTest, Comment) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"# this is a comment\n" +"foo = not # a comment\n")); + EXPECT_EQ("not # a comment", state.bindings_.LookupVariable("foo")); +} + +TEST_F(ParserTest, Dollars) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule foo\n" +" command = ${out}bar$$baz$$$\n" +"blah\n" +"x = $$dollar\n" +"build $x: foo y\n" +)); + EXPECT_EQ("$dollar", state.bindings_.LookupVariable("x")); +#ifdef _WIN32 + EXPECT_EQ("$dollarbar$baz$blah", state.edges_[0]->EvaluateCommand()); +#else + EXPECT_EQ("'$dollar'bar$baz$blah", state.edges_[0]->EvaluateCommand()); +#endif +} + +TEST_F(ParserTest, EscapeSpaces) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule spaces\n" +" command = something\n" +"build foo$ bar: spaces $$one two$$$ three\n" +)); + EXPECT_TRUE(state.LookupNode("foo bar")); + EXPECT_EQ(state.edges_[0]->outputs_[0]->path(), "foo bar"); + EXPECT_EQ(state.edges_[0]->inputs_[0]->path(), "$one"); + EXPECT_EQ(state.edges_[0]->inputs_[1]->path(), "two$ three"); + EXPECT_EQ(state.edges_[0]->EvaluateCommand(), "something"); +} + +TEST_F(ParserTest, CanonicalizeFile) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build out: cat in/1 in//2\n" +"build in/1: cat\n" +"build in/2: cat\n")); + + EXPECT_TRUE(state.LookupNode("in/1")); + EXPECT_TRUE(state.LookupNode("in/2")); + EXPECT_FALSE(state.LookupNode("in//1")); + EXPECT_FALSE(state.LookupNode("in//2")); +} + +#ifdef _WIN32 +TEST_F(ParserTest, CanonicalizeFileBackslashes) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build out: cat in\\1 in\\\\2\n" +"build in\\1: cat\n" +"build in\\2: cat\n")); + + Node* node = state.LookupNode("in/1");; + EXPECT_TRUE(node); + EXPECT_EQ(1, node->slash_bits()); + node = state.LookupNode("in/2"); + EXPECT_TRUE(node); + EXPECT_EQ(1, node->slash_bits()); + EXPECT_FALSE(state.LookupNode("in//1")); + EXPECT_FALSE(state.LookupNode("in//2")); +} +#endif + +TEST_F(ParserTest, PathVariables) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"dir = out\n" +"build $dir/exe: cat src\n")); + + EXPECT_FALSE(state.LookupNode("$dir/exe")); + EXPECT_TRUE(state.LookupNode("out/exe")); +} + +TEST_F(ParserTest, CanonicalizePaths) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build ./out.o: cat ./bar/baz/../foo.cc\n")); + + EXPECT_FALSE(state.LookupNode("./out.o")); + EXPECT_TRUE(state.LookupNode("out.o")); + EXPECT_FALSE(state.LookupNode("./bar/baz/../foo.cc")); + EXPECT_TRUE(state.LookupNode("bar/foo.cc")); +} + +#ifdef _WIN32 +TEST_F(ParserTest, CanonicalizePathsBackslashes) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build ./out.o: cat ./bar/baz/../foo.cc\n" +"build .\\out2.o: cat .\\bar/baz\\..\\foo.cc\n" +"build .\\out3.o: cat .\\bar\\baz\\..\\foo3.cc\n" +)); + + EXPECT_FALSE(state.LookupNode("./out.o")); + EXPECT_FALSE(state.LookupNode(".\\out2.o")); + EXPECT_FALSE(state.LookupNode(".\\out3.o")); + EXPECT_TRUE(state.LookupNode("out.o")); + EXPECT_TRUE(state.LookupNode("out2.o")); + EXPECT_TRUE(state.LookupNode("out3.o")); + EXPECT_FALSE(state.LookupNode("./bar/baz/../foo.cc")); + EXPECT_FALSE(state.LookupNode(".\\bar/baz\\..\\foo.cc")); + EXPECT_FALSE(state.LookupNode(".\\bar/baz\\..\\foo3.cc")); + Node* node = state.LookupNode("bar/foo.cc"); + EXPECT_TRUE(node); + EXPECT_EQ(0, node->slash_bits()); + node = state.LookupNode("bar/foo3.cc"); + EXPECT_TRUE(node); + EXPECT_EQ(1, node->slash_bits()); +} +#endif + +TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputs) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build out1 out2: cat in1\n" +"build out1: cat in2\n" +"build final: cat out1\n" +)); + // AssertParse() checks that the generated build graph is self-consistent. + // That's all the checking that this test needs. +} + +TEST_F(ParserTest, NoDeadPointerFromDuplicateEdge) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build out: cat in\n" +"build out: cat in\n" +)); + // AssertParse() checks that the generated build graph is self-consistent. + // That's all the checking that this test needs. +} + +TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputsError) { + const char kInput[] = +"rule cat\n" +" command = cat $in > $out\n" +"build out1 out2: cat in1\n" +"build out1: cat in2\n" +"build final: cat out1\n"; + ManifestParserOptions parser_opts; + parser_opts.dupe_edge_action_ = kDupeEdgeActionError; + ManifestParser parser(&state, &fs_, parser_opts); + string err; + EXPECT_FALSE(parser.ParseTest(kInput, &err)); + EXPECT_EQ("input:5: multiple rules generate out1 [-w dupbuild=err]\n", err); +} + +TEST_F(ParserTest, DuplicateEdgeInIncludedFile) { + fs_.Create("sub.ninja", + "rule cat\n" + " command = cat $in > $out\n" + "build out1 out2: cat in1\n" + "build out1: cat in2\n" + "build final: cat out1\n"); + const char kInput[] = + "subninja sub.ninja\n"; + ManifestParserOptions parser_opts; + parser_opts.dupe_edge_action_ = kDupeEdgeActionError; + ManifestParser parser(&state, &fs_, parser_opts); + string err; + EXPECT_FALSE(parser.ParseTest(kInput, &err)); + EXPECT_EQ("sub.ninja:5: multiple rules generate out1 [-w dupbuild=err]\n", + err); +} + +TEST_F(ParserTest, PhonySelfReferenceIgnored) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"build a: phony a\n" +)); + + Node* node = state.LookupNode("a"); + Edge* edge = node->in_edge(); + ASSERT_TRUE(edge->inputs_.empty()); +} + +TEST_F(ParserTest, PhonySelfReferenceKept) { + const char kInput[] = +"build a: phony a\n"; + ManifestParserOptions parser_opts; + parser_opts.phony_cycle_action_ = kPhonyCycleActionError; + ManifestParser parser(&state, &fs_, parser_opts); + string err; + EXPECT_TRUE(parser.ParseTest(kInput, &err)); + EXPECT_EQ("", err); + + Node* node = state.LookupNode("a"); + Edge* edge = node->in_edge(); + ASSERT_EQ(edge->inputs_.size(), 1); + ASSERT_EQ(edge->inputs_[0], node); +} + +TEST_F(ParserTest, ReservedWords) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule build\n" +" command = rule run $out\n" +"build subninja: build include default foo.cc\n" +"default subninja\n")); +} + +TEST_F(ParserTest, Errors) { + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest(string("subn", 4), &err)); + EXPECT_EQ("input:1: expected '=', got eof\n" + "subn\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("foobar", &err)); + EXPECT_EQ("input:1: expected '=', got eof\n" + "foobar\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("x 3", &err)); + EXPECT_EQ("input:1: expected '=', got identifier\n" + "x 3\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("x = 3", &err)); + EXPECT_EQ("input:1: unexpected EOF\n" + "x = 3\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("x = 3\ny 2", &err)); + EXPECT_EQ("input:2: expected '=', got identifier\n" + "y 2\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("x = $", &err)); + EXPECT_EQ("input:1: bad $-escape (literal $ must be written as $$)\n" + "x = $\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("x = $\n $[\n", &err)); + EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" + " $[\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("x = a$\n b$\n $\n", &err)); + EXPECT_EQ("input:4: unexpected EOF\n" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("build\n", &err)); + EXPECT_EQ("input:1: expected path\n" + "build\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err)); + EXPECT_EQ("input:1: unknown build rule 'y'\n" + "build x: y z\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("build x:: y z\n", &err)); + EXPECT_EQ("input:1: expected build command name\n" + "build x:: y z\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cat\n command = cat ok\n" + "build x: cat $\n :\n", + &err)); + EXPECT_EQ("input:4: expected newline, got ':'\n" + " :\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cat\n", + &err)); + EXPECT_EQ("input:2: expected 'command =' line\n", err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cat\n" + " command = echo\n" + "rule cat\n" + " command = echo\n", &err)); + EXPECT_EQ("input:3: duplicate rule 'cat'\n" + "rule cat\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cat\n" + " command = echo\n" + " rspfile = cat.rsp\n", &err)); + EXPECT_EQ( + "input:4: rspfile and rspfile_content need to be both specified\n", + err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cat\n" + " command = ${fafsd\n" + "foo = bar\n", + &err)); + EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" + " command = ${fafsd\n" + " ^ near here" + , err); + } + + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cat\n" + " command = cat\n" + "build $.: cat foo\n", + &err)); + EXPECT_EQ("input:3: bad $-escape (literal $ must be written as $$)\n" + "build $.: cat foo\n" + " ^ near here" + , err); + } + + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cat\n" + " command = cat\n" + "build $: cat foo\n", + &err)); + EXPECT_EQ("input:3: expected ':', got newline ($ also escapes ':')\n" + "build $: cat foo\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule %foo\n", + &err)); + EXPECT_EQ("input:1: expected rule name\n", err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cc\n" + " command = foo\n" + " othervar = bar\n", + &err)); + EXPECT_EQ("input:3: unexpected variable 'othervar'\n" + " othervar = bar\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" + "build $.: cc bar.cc\n", + &err)); + EXPECT_EQ("input:3: bad $-escape (literal $ must be written as $$)\n" + "build $.: cc bar.cc\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n && bar", + &err)); + EXPECT_EQ("input:3: expected variable name\n", err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" + "build $: cc bar.cc\n", + &err)); + EXPECT_EQ("input:3: expected ':', got newline ($ also escapes ':')\n" + "build $: cc bar.cc\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("default\n", + &err)); + EXPECT_EQ("input:1: expected target name\n" + "default\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("default nonexistent\n", + &err)); + EXPECT_EQ("input:1: unknown target 'nonexistent'\n" + "default nonexistent\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule r\n command = r\n" + "build b: r\n" + "default b:\n", + &err)); + EXPECT_EQ("input:4: expected newline, got ':'\n" + "default b:\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("default $a\n", &err)); + EXPECT_EQ("input:1: empty path\n" + "default $a\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule r\n" + " command = r\n" + "build $a: r $c\n", &err)); + // XXX the line number is wrong; we should evaluate paths in ParseEdge + // as we see them, not after we've read them all! + EXPECT_EQ("input:4: empty path\n", err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + // the indented blank line must terminate the rule + // this also verifies that "unexpected (token)" errors are correct + EXPECT_FALSE(parser.ParseTest("rule r\n" + " command = r\n" + " \n" + " generator = 1\n", &err)); + EXPECT_EQ("input:4: unexpected indent\n", err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("pool\n", &err)); + EXPECT_EQ("input:1: expected pool name\n", err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("pool foo\n", &err)); + EXPECT_EQ("input:2: expected 'depth =' line\n", err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("pool foo\n" + " depth = 4\n" + "pool foo\n", &err)); + EXPECT_EQ("input:3: duplicate pool 'foo'\n" + "pool foo\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("pool foo\n" + " depth = -1\n", &err)); + EXPECT_EQ("input:2: invalid pool depth\n" + " depth = -1\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("pool foo\n" + " bar = 1\n", &err)); + EXPECT_EQ("input:2: unexpected variable 'bar'\n" + " bar = 1\n" + " ^ near here" + , err); + } + + { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + // Pool names are dereferenced at edge parsing time. + EXPECT_FALSE(parser.ParseTest("rule run\n" + " command = echo\n" + " pool = unnamed_pool\n" + "build out: run in\n", &err)); + EXPECT_EQ("input:5: unknown pool name 'unnamed_pool'\n", err); + } +} + +TEST_F(ParserTest, MissingInput) { + State local_state; + ManifestParser parser(&local_state, &fs_); + string err; + EXPECT_FALSE(parser.Load("build.ninja", &err)); + EXPECT_EQ("loading 'build.ninja': No such file or directory", err); +} + +TEST_F(ParserTest, MultipleOutputs) { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_TRUE(parser.ParseTest("rule cc\n command = foo\n depfile = bar\n" + "build a.o b.o: cc c.cc\n", + &err)); + EXPECT_EQ("", err); +} + +TEST_F(ParserTest, MultipleOutputsWithDeps) { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n deps = gcc\n" + "build a.o b.o: cc c.cc\n", + &err)); + EXPECT_EQ("input:5: multiple outputs aren't (yet?) supported by depslog; " + "bring this up on the mailing list if it affects you\n", err); +} + +TEST_F(ParserTest, SubNinja) { + fs_.Create("test.ninja", + "var = inner\n" + "build $builddir/inner: varref\n"); + ASSERT_NO_FATAL_FAILURE(AssertParse( +"builddir = some_dir/\n" +"rule varref\n" +" command = varref $var\n" +"var = outer\n" +"build $builddir/outer: varref\n" +"subninja test.ninja\n" +"build $builddir/outer2: varref\n")); + ASSERT_EQ(1u, fs_.files_read_.size()); + + EXPECT_EQ("test.ninja", fs_.files_read_[0]); + EXPECT_TRUE(state.LookupNode("some_dir/outer")); + // Verify our builddir setting is inherited. + EXPECT_TRUE(state.LookupNode("some_dir/inner")); + + ASSERT_EQ(3u, state.edges_.size()); + EXPECT_EQ("varref outer", state.edges_[0]->EvaluateCommand()); + EXPECT_EQ("varref inner", state.edges_[1]->EvaluateCommand()); + EXPECT_EQ("varref outer", state.edges_[2]->EvaluateCommand()); +} + +TEST_F(ParserTest, MissingSubNinja) { + ManifestParser parser(&state, &fs_); + string err; + EXPECT_FALSE(parser.ParseTest("subninja foo.ninja\n", &err)); + EXPECT_EQ("input:1: loading 'foo.ninja': No such file or directory\n" + "subninja foo.ninja\n" + " ^ near here" + , err); +} + +TEST_F(ParserTest, DuplicateRuleInDifferentSubninjas) { + // Test that rules are scoped to subninjas. + fs_.Create("test.ninja", "rule cat\n" + " command = cat\n"); + ManifestParser parser(&state, &fs_); + string err; + EXPECT_TRUE(parser.ParseTest("rule cat\n" + " command = cat\n" + "subninja test.ninja\n", &err)); +} + +TEST_F(ParserTest, DuplicateRuleInDifferentSubninjasWithInclude) { + // Test that rules are scoped to subninjas even with includes. + fs_.Create("rules.ninja", "rule cat\n" + " command = cat\n"); + fs_.Create("test.ninja", "include rules.ninja\n" + "build x : cat\n"); + ManifestParser parser(&state, &fs_); + string err; + EXPECT_TRUE(parser.ParseTest("include rules.ninja\n" + "subninja test.ninja\n" + "build y : cat\n", &err)); +} + +TEST_F(ParserTest, Include) { + fs_.Create("include.ninja", "var = inner\n"); + ASSERT_NO_FATAL_FAILURE(AssertParse( +"var = outer\n" +"include include.ninja\n")); + + ASSERT_EQ(1u, fs_.files_read_.size()); + EXPECT_EQ("include.ninja", fs_.files_read_[0]); + EXPECT_EQ("inner", state.bindings_.LookupVariable("var")); +} + +TEST_F(ParserTest, BrokenInclude) { + fs_.Create("include.ninja", "build\n"); + ManifestParser parser(&state, &fs_); + string err; + EXPECT_FALSE(parser.ParseTest("include include.ninja\n", &err)); + EXPECT_EQ("include.ninja:1: expected path\n" + "build\n" + " ^ near here" + , err); +} + +TEST_F(ParserTest, Implicit) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo: cat bar | baz\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_TRUE(edge->is_implicit(1)); +} + +TEST_F(ParserTest, OrderOnly) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n command = cat $in > $out\n" +"build foo: cat bar || baz\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_TRUE(edge->is_order_only(1)); +} + +TEST_F(ParserTest, ImplicitOutput) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo | imp: cat bar\n")); + + Edge* edge = state.LookupNode("imp")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 2); + EXPECT_TRUE(edge->is_implicit_out(1)); +} + +TEST_F(ParserTest, ImplicitOutputEmpty) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo | : cat bar\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 1); + EXPECT_FALSE(edge->is_implicit_out(0)); +} + +TEST_F(ParserTest, ImplicitOutputDupe) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo baz | foo baq foo: cat bar\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 3); + EXPECT_FALSE(edge->is_implicit_out(0)); + EXPECT_FALSE(edge->is_implicit_out(1)); + EXPECT_TRUE(edge->is_implicit_out(2)); +} + +TEST_F(ParserTest, ImplicitOutputDupes) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo foo foo | foo foo foo foo: cat bar\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 1); + EXPECT_FALSE(edge->is_implicit_out(0)); +} + +TEST_F(ParserTest, NoExplicitOutput) { + ManifestParser parser(&state, NULL); + string err; + EXPECT_TRUE(parser.ParseTest( +"rule cat\n" +" command = cat $in > $out\n" +"build | imp : cat bar\n", &err)); +} + +TEST_F(ParserTest, DefaultDefault) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n command = cat $in > $out\n" +"build a: cat foo\n" +"build b: cat foo\n" +"build c: cat foo\n" +"build d: cat foo\n")); + + string err; + EXPECT_EQ(4u, state.DefaultNodes(&err).size()); + EXPECT_EQ("", err); +} + +TEST_F(ParserTest, DefaultDefaultCycle) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n command = cat $in > $out\n" +"build a: cat a\n")); + + string err; + EXPECT_EQ(0u, state.DefaultNodes(&err).size()); + EXPECT_EQ("could not determine root nodes of build graph", err); +} + +TEST_F(ParserTest, DefaultStatements) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n command = cat $in > $out\n" +"build a: cat foo\n" +"build b: cat foo\n" +"build c: cat foo\n" +"build d: cat foo\n" +"third = c\n" +"default a b\n" +"default $third\n")); + + string err; + vector nodes = state.DefaultNodes(&err); + EXPECT_EQ("", err); + ASSERT_EQ(3u, nodes.size()); + EXPECT_EQ("a", nodes[0]->path()); + EXPECT_EQ("b", nodes[1]->path()); + EXPECT_EQ("c", nodes[2]->path()); +} + +TEST_F(ParserTest, UTF8) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule utf8\n" +" command = true\n" +" description = compilaci\xC3\xB3\n")); +} + +TEST_F(ParserTest, CRLF) { + State local_state; + ManifestParser parser(&local_state, NULL); + string err; + + EXPECT_TRUE(parser.ParseTest("# comment with crlf\r\n", &err)); + EXPECT_TRUE(parser.ParseTest("foo = foo\nbar = bar\r\n", &err)); + EXPECT_TRUE(parser.ParseTest( + "pool link_pool\r\n" + " depth = 15\r\n\r\n" + "rule xyz\r\n" + " command = something$expand \r\n" + " description = YAY!\r\n", + &err)); +} diff --git a/src/3rdparty/ninja/src/metrics.cc b/src/3rdparty/ninja/src/metrics.cc new file mode 100644 index 00000000000..a7d3c7ad5b9 --- /dev/null +++ b/src/3rdparty/ninja/src/metrics.cc @@ -0,0 +1,127 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "metrics.h" + +#include +#include +#include + +#ifndef _WIN32 +#include +#else +#include +#endif + +#include + +#include "util.h" + +Metrics* g_metrics = NULL; + +namespace { + +#ifndef _WIN32 +/// Compute a platform-specific high-res timer value that fits into an int64. +int64_t HighResTimer() { + timeval tv; + if (gettimeofday(&tv, NULL) < 0) + Fatal("gettimeofday: %s", strerror(errno)); + return (int64_t)tv.tv_sec * 1000*1000 + tv.tv_usec; +} + +/// Convert a delta of HighResTimer() values to microseconds. +int64_t TimerToMicros(int64_t dt) { + // No conversion necessary. + return dt; +} +#else +int64_t LargeIntegerToInt64(const LARGE_INTEGER& i) { + return ((int64_t)i.HighPart) << 32 | i.LowPart; +} + +int64_t HighResTimer() { + LARGE_INTEGER counter; + if (!QueryPerformanceCounter(&counter)) + Fatal("QueryPerformanceCounter: %s", GetLastErrorString().c_str()); + return LargeIntegerToInt64(counter); +} + +int64_t TimerToMicros(int64_t dt) { + static int64_t ticks_per_sec = 0; + if (!ticks_per_sec) { + LARGE_INTEGER freq; + if (!QueryPerformanceFrequency(&freq)) + Fatal("QueryPerformanceFrequency: %s", GetLastErrorString().c_str()); + ticks_per_sec = LargeIntegerToInt64(freq); + } + + // dt is in ticks. We want microseconds. + return (dt * 1000000) / ticks_per_sec; +} +#endif + +} // anonymous namespace + + +ScopedMetric::ScopedMetric(Metric* metric) { + metric_ = metric; + if (!metric_) + return; + start_ = HighResTimer(); +} +ScopedMetric::~ScopedMetric() { + if (!metric_) + return; + metric_->count++; + int64_t dt = TimerToMicros(HighResTimer() - start_); + metric_->sum += dt; +} + +Metric* Metrics::NewMetric(const string& name) { + Metric* metric = new Metric; + metric->name = name; + metric->count = 0; + metric->sum = 0; + metrics_.push_back(metric); + return metric; +} + +void Metrics::Report() { + int width = 0; + for (vector::iterator i = metrics_.begin(); + i != metrics_.end(); ++i) { + width = max((int)(*i)->name.size(), width); + } + + printf("%-*s\t%-6s\t%-9s\t%s\n", width, + "metric", "count", "avg (us)", "total (ms)"); + for (vector::iterator i = metrics_.begin(); + i != metrics_.end(); ++i) { + Metric* metric = *i; + double total = metric->sum / (double)1000; + double avg = metric->sum / (double)metric->count; + printf("%-*s\t%-6d\t%-8.1f\t%.1f\n", width, metric->name.c_str(), + metric->count, avg, total); + } +} + +uint64_t Stopwatch::Now() const { + return TimerToMicros(HighResTimer()); +} + +int64_t GetTimeMillis() { + return TimerToMicros(HighResTimer()) / 1000; +} + diff --git a/src/3rdparty/ninja/src/metrics.h b/src/3rdparty/ninja/src/metrics.h new file mode 100644 index 00000000000..b6da859db2b --- /dev/null +++ b/src/3rdparty/ninja/src/metrics.h @@ -0,0 +1,92 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_METRICS_H_ +#define NINJA_METRICS_H_ + +#include +#include +using namespace std; + +#include "util.h" // For int64_t. + +/// The Metrics module is used for the debug mode that dumps timing stats of +/// various actions. To use, see METRIC_RECORD below. + +/// A single metrics we're tracking, like "depfile load time". +struct Metric { + string name; + /// Number of times we've hit the code path. + int count; + /// Total time (in micros) we've spent on the code path. + int64_t sum; +}; + + +/// A scoped object for recording a metric across the body of a function. +/// Used by the METRIC_RECORD macro. +struct ScopedMetric { + explicit ScopedMetric(Metric* metric); + ~ScopedMetric(); + +private: + Metric* metric_; + /// Timestamp when the measurement started. + /// Value is platform-dependent. + int64_t start_; +}; + +/// The singleton that stores metrics and prints the report. +struct Metrics { + Metric* NewMetric(const string& name); + + /// Print a summary report to stdout. + void Report(); + +private: + vector metrics_; +}; + +/// Get the current time as relative to some epoch. +/// Epoch varies between platforms; only useful for measuring elapsed time. +int64_t GetTimeMillis(); + +/// A simple stopwatch which returns the time +/// in seconds since Restart() was called. +struct Stopwatch { + public: + Stopwatch() : started_(0) {} + + /// Seconds since Restart() call. + double Elapsed() const { + return 1e-6 * static_cast(Now() - started_); + } + + void Restart() { started_ = Now(); } + + private: + uint64_t started_; + uint64_t Now() const; +}; + +/// The primary interface to metrics. Use METRIC_RECORD("foobar") at the top +/// of a function to get timing stats recorded for each call of the function. +#define METRIC_RECORD(name) \ + static Metric* metrics_h_metric = \ + g_metrics ? g_metrics->NewMetric(name) : NULL; \ + ScopedMetric metrics_h_scoped(metrics_h_metric); + +extern Metrics* g_metrics; + +#endif // NINJA_METRICS_H_ diff --git a/src/3rdparty/ninja/src/minidump-win32.cc b/src/3rdparty/ninja/src/minidump-win32.cc new file mode 100644 index 00000000000..1efb085a9c1 --- /dev/null +++ b/src/3rdparty/ninja/src/minidump-win32.cc @@ -0,0 +1,87 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef _MSC_VER + +#include +#include + +#include "util.h" + +typedef BOOL (WINAPI *MiniDumpWriteDumpFunc) ( + IN HANDLE, + IN DWORD, + IN HANDLE, + IN MINIDUMP_TYPE, + IN CONST PMINIDUMP_EXCEPTION_INFORMATION, OPTIONAL + IN CONST PMINIDUMP_USER_STREAM_INFORMATION, OPTIONAL + IN CONST PMINIDUMP_CALLBACK_INFORMATION OPTIONAL + ); + +/// Creates a windows minidump in temp folder. +void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep) { + char temp_path[MAX_PATH]; + GetTempPath(sizeof(temp_path), temp_path); + char temp_file[MAX_PATH]; + sprintf(temp_file, "%s\\ninja_crash_dump_%lu.dmp", + temp_path, GetCurrentProcessId()); + + // Delete any previous minidump of the same name. + DeleteFile(temp_file); + + // Load DbgHelp.dll dynamically, as library is not present on all + // Windows versions. + HMODULE dbghelp = LoadLibrary("dbghelp.dll"); + if (dbghelp == NULL) { + Error("failed to create minidump: LoadLibrary('dbghelp.dll'): %s", + GetLastErrorString().c_str()); + return; + } + + MiniDumpWriteDumpFunc mini_dump_write_dump = + (MiniDumpWriteDumpFunc)GetProcAddress(dbghelp, "MiniDumpWriteDump"); + if (mini_dump_write_dump == NULL) { + Error("failed to create minidump: GetProcAddress('MiniDumpWriteDump'): %s", + GetLastErrorString().c_str()); + return; + } + + HANDLE hFile = CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == NULL) { + Error("failed to create minidump: CreateFileA(%s): %s", + temp_file, GetLastErrorString().c_str()); + return; + } + + MINIDUMP_EXCEPTION_INFORMATION mdei; + mdei.ThreadId = GetCurrentThreadId(); + mdei.ExceptionPointers = pep; + mdei.ClientPointers = FALSE; + MINIDUMP_TYPE mdt = (MINIDUMP_TYPE) (MiniDumpWithDataSegs | + MiniDumpWithHandleData); + + BOOL rv = mini_dump_write_dump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0); + CloseHandle(hFile); + + if (!rv) { + Error("MiniDumpWriteDump failed: %s", GetLastErrorString().c_str()); + return; + } + + Warning("minidump created: %s", temp_file); +} + +#endif // _MSC_VER diff --git a/src/3rdparty/ninja/src/msvc_helper-win32.cc b/src/3rdparty/ninja/src/msvc_helper-win32.cc new file mode 100644 index 00000000000..e37a26ea603 --- /dev/null +++ b/src/3rdparty/ninja/src/msvc_helper-win32.cc @@ -0,0 +1,106 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "msvc_helper.h" + +#include + +#include "util.h" + +namespace { + +string Replace(const string& input, const string& find, const string& replace) { + string result = input; + size_t start_pos = 0; + while ((start_pos = result.find(find, start_pos)) != string::npos) { + result.replace(start_pos, find.length(), replace); + start_pos += replace.length(); + } + return result; +} + +} // anonymous namespace + +string EscapeForDepfile(const string& path) { + // Depfiles don't escape single \. + return Replace(path, " ", "\\ "); +} + +int CLWrapper::Run(const string& command, string* output) { + SECURITY_ATTRIBUTES security_attributes = {}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = TRUE; + + // Must be inheritable so subprocesses can dup to children. + HANDLE nul = CreateFile("NUL", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, 0, NULL); + if (nul == INVALID_HANDLE_VALUE) + Fatal("couldn't open nul"); + + HANDLE stdout_read, stdout_write; + if (!CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)) + Win32Fatal("CreatePipe"); + + if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) + Win32Fatal("SetHandleInformation"); + + PROCESS_INFORMATION process_info = {}; + STARTUPINFO startup_info = {}; + startup_info.cb = sizeof(STARTUPINFO); + startup_info.hStdInput = nul; + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + startup_info.hStdOutput = stdout_write; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + + if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL, + /* inherit handles */ TRUE, 0, + env_block_, NULL, + &startup_info, &process_info)) { + Win32Fatal("CreateProcess"); + } + + if (!CloseHandle(nul) || + !CloseHandle(stdout_write)) { + Win32Fatal("CloseHandle"); + } + + // Read all output of the subprocess. + DWORD read_len = 1; + while (read_len) { + char buf[64 << 10]; + read_len = 0; + if (!::ReadFile(stdout_read, buf, sizeof(buf), &read_len, NULL) && + GetLastError() != ERROR_BROKEN_PIPE) { + Win32Fatal("ReadFile"); + } + output->append(buf, read_len); + } + + // Wait for it to exit and grab its exit code. + if (WaitForSingleObject(process_info.hProcess, INFINITE) == WAIT_FAILED) + Win32Fatal("WaitForSingleObject"); + DWORD exit_code = 0; + if (!GetExitCodeProcess(process_info.hProcess, &exit_code)) + Win32Fatal("GetExitCodeProcess"); + + if (!CloseHandle(stdout_read) || + !CloseHandle(process_info.hProcess) || + !CloseHandle(process_info.hThread)) { + Win32Fatal("CloseHandle"); + } + + return exit_code; +} diff --git a/src/3rdparty/ninja/src/msvc_helper.h b/src/3rdparty/ninja/src/msvc_helper.h new file mode 100644 index 00000000000..70d1fff794f --- /dev/null +++ b/src/3rdparty/ninja/src/msvc_helper.h @@ -0,0 +1,33 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +using namespace std; + +string EscapeForDepfile(const string& path); + +/// Wraps a synchronous execution of a CL subprocess. +struct CLWrapper { + CLWrapper() : env_block_(NULL) {} + + /// Set the environment block (as suitable for CreateProcess) to be used + /// by Run(). + void SetEnvBlock(void* env_block) { env_block_ = env_block; } + + /// Start a process and gather its raw output. Returns its exit code. + /// Crashes (calls Fatal()) on error. + int Run(const string& command, string* output); + + void* env_block_; +}; diff --git a/src/3rdparty/ninja/src/msvc_helper_main-win32.cc b/src/3rdparty/ninja/src/msvc_helper_main-win32.cc new file mode 100644 index 00000000000..e419cd7742b --- /dev/null +++ b/src/3rdparty/ninja/src/msvc_helper_main-win32.cc @@ -0,0 +1,148 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "msvc_helper.h" + +#include +#include +#include +#include + +#include "clparser.h" +#include "util.h" + +#include "getopt.h" + +namespace { + +void Usage() { + printf( +"usage: ninja -t msvc [options] -- cl.exe /showIncludes /otherArgs\n" +"options:\n" +" -e ENVFILE load environment block from ENVFILE as environment\n" +" -o FILE write output dependency information to FILE.d\n" +" -p STRING localized prefix of msvc's /showIncludes output\n" + ); +} + +void PushPathIntoEnvironment(const string& env_block) { + const char* as_str = env_block.c_str(); + while (as_str[0]) { + if (_strnicmp(as_str, "path=", 5) == 0) { + _putenv(as_str); + return; + } else { + as_str = &as_str[strlen(as_str) + 1]; + } + } +} + +void WriteDepFileOrDie(const char* object_path, const CLParser& parse) { + string depfile_path = string(object_path) + ".d"; + FILE* depfile = fopen(depfile_path.c_str(), "w"); + if (!depfile) { + unlink(object_path); + Fatal("opening %s: %s", depfile_path.c_str(), + GetLastErrorString().c_str()); + } + if (fprintf(depfile, "%s: ", object_path) < 0) { + unlink(object_path); + fclose(depfile); + unlink(depfile_path.c_str()); + Fatal("writing %s", depfile_path.c_str()); + } + const set& headers = parse.includes_; + for (set::const_iterator i = headers.begin(); + i != headers.end(); ++i) { + if (fprintf(depfile, "%s\n", EscapeForDepfile(*i).c_str()) < 0) { + unlink(object_path); + fclose(depfile); + unlink(depfile_path.c_str()); + Fatal("writing %s", depfile_path.c_str()); + } + } + fclose(depfile); +} + +} // anonymous namespace + +int MSVCHelperMain(int argc, char** argv) { + const char* output_filename = NULL; + const char* envfile = NULL; + + const option kLongOptions[] = { + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + int opt; + string deps_prefix; + while ((opt = getopt_long(argc, argv, "e:o:p:h", kLongOptions, NULL)) != -1) { + switch (opt) { + case 'e': + envfile = optarg; + break; + case 'o': + output_filename = optarg; + break; + case 'p': + deps_prefix = optarg; + break; + case 'h': + default: + Usage(); + return 0; + } + } + + string env; + if (envfile) { + string err; + if (ReadFile(envfile, &env, &err) != 0) + Fatal("couldn't open %s: %s", envfile, err.c_str()); + PushPathIntoEnvironment(env); + } + + char* command = GetCommandLine(); + command = strstr(command, " -- "); + if (!command) { + Fatal("expected command line to end with \" -- command args\""); + } + command += 4; + + CLWrapper cl; + if (!env.empty()) + cl.SetEnvBlock((void*)env.data()); + string output; + int exit_code = cl.Run(command, &output); + + if (output_filename) { + CLParser parser; + string err; + if (!parser.Parse(output, deps_prefix, &output, &err)) + Fatal("%s\n", err.c_str()); + WriteDepFileOrDie(output_filename, parser); + } + + if (output.empty()) + return exit_code; + + // CLWrapper's output already as \r\n line endings, make sure the C runtime + // doesn't expand this to \r\r\n. + _setmode(_fileno(stdout), _O_BINARY); + // Avoid printf and C strings, since the actual output might contain null + // bytes like UTF-16 does (yuck). + fwrite(&output[0], 1, output.size(), stdout); + + return exit_code; +} diff --git a/src/3rdparty/ninja/src/msvc_helper_test.cc b/src/3rdparty/ninja/src/msvc_helper_test.cc new file mode 100644 index 00000000000..eaae51f5645 --- /dev/null +++ b/src/3rdparty/ninja/src/msvc_helper_test.cc @@ -0,0 +1,39 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "msvc_helper.h" + +#include "test.h" +#include "util.h" + +TEST(EscapeForDepfileTest, SpacesInFilename) { + ASSERT_EQ("sub\\some\\ sdk\\foo.h", + EscapeForDepfile("sub\\some sdk\\foo.h")); +} + +TEST(MSVCHelperTest, EnvBlock) { + char env_block[] = "foo=bar\0"; + CLWrapper cl; + cl.SetEnvBlock(env_block); + string output; + cl.Run("cmd /c \"echo foo is %foo%", &output); + ASSERT_EQ("foo is bar\r\n", output); +} + +TEST(MSVCHelperTest, NoReadOfStderr) { + CLWrapper cl; + string output; + cl.Run("cmd /c \"echo to stdout&& echo to stderr 1>&2", &output); + ASSERT_EQ("to stdout\r\n", output); +} diff --git a/src/3rdparty/ninja/src/ninja.cc b/src/3rdparty/ninja/src/ninja.cc new file mode 100644 index 00000000000..ed004ac8f1f --- /dev/null +++ b/src/3rdparty/ninja/src/ninja.cc @@ -0,0 +1,1228 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include "getopt.h" +#include +#include +#elif defined(_AIX) +#include "getopt.h" +#include +#else +#include +#include +#endif + +#include "browse.h" +#include "build.h" +#include "build_log.h" +#include "deps_log.h" +#include "clean.h" +#include "debug_flags.h" +#include "disk_interface.h" +#include "graph.h" +#include "graphviz.h" +#include "manifest_parser.h" +#include "metrics.h" +#include "state.h" +#include "util.h" +#include "version.h" + +#ifdef _MSC_VER +// Defined in msvc_helper_main-win32.cc. +int MSVCHelperMain(int argc, char** argv); + +// Defined in minidump-win32.cc. +void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep); +#endif + +namespace { + +struct Tool; + +/// Command-line options. +struct Options { + /// Build file to load. + const char* input_file; + + /// Directory to change into before running. + const char* working_dir; + + /// Tool to run rather than building. + const Tool* tool; + + /// Whether duplicate rules for one target should warn or print an error. + bool dupe_edges_should_err; + + /// Whether phony cycles should warn or print an error. + bool phony_cycle_should_err; +}; + +/// The Ninja main() loads up a series of data structures; various tools need +/// to poke into these, so store them as fields on an object. +struct NinjaMain : public BuildLogUser { + NinjaMain(const char* ninja_command, const BuildConfig& config) : + ninja_command_(ninja_command), config_(config) {} + + /// Command line used to run Ninja. + const char* ninja_command_; + + /// Build configuration set from flags (e.g. parallelism). + const BuildConfig& config_; + + /// Loaded state (rules, nodes). + State state_; + + /// Functions for accesssing the disk. + RealDiskInterface disk_interface_; + + /// The build directory, used for storing the build log etc. + string build_dir_; + + BuildLog build_log_; + DepsLog deps_log_; + + /// The type of functions that are the entry points to tools (subcommands). + typedef int (NinjaMain::*ToolFunc)(const Options*, int, char**); + + /// Get the Node for a given command-line path, handling features like + /// spell correction. + Node* CollectTarget(const char* cpath, string* err); + + /// CollectTarget for all command-line arguments, filling in \a targets. + bool CollectTargetsFromArgs(int argc, char* argv[], + vector* targets, string* err); + + // The various subcommands, run via "-t XXX". + int ToolGraph(const Options* options, int argc, char* argv[]); + int ToolQuery(const Options* options, int argc, char* argv[]); + int ToolDeps(const Options* options, int argc, char* argv[]); + int ToolBrowse(const Options* options, int argc, char* argv[]); + int ToolMSVC(const Options* options, int argc, char* argv[]); + int ToolTargets(const Options* options, int argc, char* argv[]); + int ToolCommands(const Options* options, int argc, char* argv[]); + int ToolClean(const Options* options, int argc, char* argv[]); + int ToolCompilationDatabase(const Options* options, int argc, char* argv[]); + int ToolRecompact(const Options* options, int argc, char* argv[]); + int ToolUrtle(const Options* options, int argc, char** argv); + + /// Open the build log. + /// @return false on error. + bool OpenBuildLog(bool recompact_only = false); + + /// Open the deps log: load it, then open for writing. + /// @return false on error. + bool OpenDepsLog(bool recompact_only = false); + + /// Ensure the build directory exists, creating it if necessary. + /// @return false on error. + bool EnsureBuildDirExists(); + + /// Rebuild the manifest, if necessary. + /// Fills in \a err on error. + /// @return true if the manifest was rebuilt. + bool RebuildManifest(const char* input_file, string* err); + + /// Build the targets listed on the command line. + /// @return an exit code. + int RunBuild(int argc, char** argv); + + /// Dump the output requested by '-d stats'. + void DumpMetrics(); + + virtual bool IsPathDead(StringPiece s) const { + Node* n = state_.LookupNode(s); + if (!n || !n->in_edge()) + return false; + // Just checking n isn't enough: If an old output is both in the build log + // and in the deps log, it will have a Node object in state_. (It will also + // have an in edge if one of its inputs is another output that's in the deps + // log, but having a deps edge product an output thats input to another deps + // edge is rare, and the first recompaction will delete all old outputs from + // the deps log, and then a second recompaction will clear the build log, + // which seems good enough for this corner case.) + // Do keep entries around for files which still exist on disk, for + // generators that want to use this information. + string err; + TimeStamp mtime = disk_interface_.Stat(s.AsString(), &err); + if (mtime == -1) + Error("%s", err.c_str()); // Log and ignore Stat() errors. + return mtime == 0; + } +}; + +/// Subtools, accessible via "-t foo". +struct Tool { + /// Short name of the tool. + const char* name; + + /// Description (shown in "-t list"). + const char* desc; + + /// When to run the tool. + enum { + /// Run after parsing the command-line flags and potentially changing + /// the current working directory (as early as possible). + RUN_AFTER_FLAGS, + + /// Run after loading build.ninja. + RUN_AFTER_LOAD, + + /// Run after loading the build/deps logs. + RUN_AFTER_LOGS, + } when; + + /// Implementation of the tool. + NinjaMain::ToolFunc func; +}; + +/// Print usage information. +void Usage(const BuildConfig& config) { + fprintf(stderr, +"usage: ninja [options] [targets...]\n" +"\n" +"if targets are unspecified, builds the 'default' target (see manual).\n" +"\n" +"options:\n" +" --version print ninja version (\"%s\")\n" +"\n" +" -C DIR change to DIR before doing anything else\n" +" -f FILE specify input build file [default=build.ninja]\n" +"\n" +" -j N run N jobs in parallel [default=%d, derived from CPUs available]\n" +" -k N keep going until N jobs fail [default=1]\n" +" -l N do not start new jobs if the load average is greater than N\n" +" -n dry run (don't run commands but act like they succeeded)\n" +" -v show all command lines while building\n" +"\n" +" -d MODE enable debugging (use -d list to list modes)\n" +" -t TOOL run a subtool (use -t list to list subtools)\n" +" terminates toplevel options; further flags are passed to the tool\n" +" -w FLAG adjust warnings (use -w list to list warnings)\n", + kNinjaVersion, config.parallelism); +} + +/// Choose a default value for the -j (parallelism) flag. +int GuessParallelism() { + switch (int processors = GetProcessorCount()) { + case 0: + case 1: + return 2; + case 2: + return 3; + default: + return processors + 2; + } +} + +/// Rebuild the build manifest, if necessary. +/// Returns true if the manifest was rebuilt. +bool NinjaMain::RebuildManifest(const char* input_file, string* err) { + string path = input_file; + uint64_t slash_bits; // Unused because this path is only used for lookup. + if (!CanonicalizePath(&path, &slash_bits, err)) + return false; + Node* node = state_.LookupNode(path); + if (!node) + return false; + + Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_); + if (!builder.AddTarget(node, err)) + return false; + + if (builder.AlreadyUpToDate()) + return false; // Not an error, but we didn't rebuild. + + if (!builder.Build(err)) + return false; + + // The manifest was only rebuilt if it is now dirty (it may have been cleaned + // by a restat). + if (!node->dirty()) { + // Reset the state to prevent problems like + // https://github.com/ninja-build/ninja/issues/874 + state_.Reset(); + return false; + } + + return true; +} + +Node* NinjaMain::CollectTarget(const char* cpath, string* err) { + string path = cpath; + uint64_t slash_bits; + if (!CanonicalizePath(&path, &slash_bits, err)) + return NULL; + + // Special syntax: "foo.cc^" means "the first output of foo.cc". + bool first_dependent = false; + if (!path.empty() && path[path.size() - 1] == '^') { + path.resize(path.size() - 1); + first_dependent = true; + } + + Node* node = state_.LookupNode(path); + if (node) { + if (first_dependent) { + if (node->out_edges().empty()) { + *err = "'" + path + "' has no out edge"; + return NULL; + } + Edge* edge = node->out_edges()[0]; + if (edge->outputs_.empty()) { + edge->Dump(); + Fatal("edge has no outputs"); + } + node = edge->outputs_[0]; + } + return node; + } else { + *err = + "unknown target '" + Node::PathDecanonicalized(path, slash_bits) + "'"; + if (path == "clean") { + *err += ", did you mean 'ninja -t clean'?"; + } else if (path == "help") { + *err += ", did you mean 'ninja -h'?"; + } else { + Node* suggestion = state_.SpellcheckNode(path); + if (suggestion) { + *err += ", did you mean '" + suggestion->path() + "'?"; + } + } + return NULL; + } +} + +bool NinjaMain::CollectTargetsFromArgs(int argc, char* argv[], + vector* targets, string* err) { + if (argc == 0) { + *targets = state_.DefaultNodes(err); + return err->empty(); + } + + for (int i = 0; i < argc; ++i) { + Node* node = CollectTarget(argv[i], err); + if (node == NULL) + return false; + targets->push_back(node); + } + return true; +} + +int NinjaMain::ToolGraph(const Options* options, int argc, char* argv[]) { + vector nodes; + string err; + if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { + Error("%s", err.c_str()); + return 1; + } + + GraphViz graph; + graph.Start(); + for (vector::const_iterator n = nodes.begin(); n != nodes.end(); ++n) + graph.AddTarget(*n); + graph.Finish(); + + return 0; +} + +int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) { + if (argc == 0) { + Error("expected a target to query"); + return 1; + } + + for (int i = 0; i < argc; ++i) { + string err; + Node* node = CollectTarget(argv[i], &err); + if (!node) { + Error("%s", err.c_str()); + return 1; + } + + printf("%s:\n", node->path().c_str()); + if (Edge* edge = node->in_edge()) { + printf(" input: %s\n", edge->rule_->name().c_str()); + for (int in = 0; in < (int)edge->inputs_.size(); in++) { + const char* label = ""; + if (edge->is_implicit(in)) + label = "| "; + else if (edge->is_order_only(in)) + label = "|| "; + printf(" %s%s\n", label, edge->inputs_[in]->path().c_str()); + } + } + printf(" outputs:\n"); + for (vector::const_iterator edge = node->out_edges().begin(); + edge != node->out_edges().end(); ++edge) { + for (vector::iterator out = (*edge)->outputs_.begin(); + out != (*edge)->outputs_.end(); ++out) { + printf(" %s\n", (*out)->path().c_str()); + } + } + } + return 0; +} + +#if defined(NINJA_HAVE_BROWSE) +int NinjaMain::ToolBrowse(const Options* options, int argc, char* argv[]) { + RunBrowsePython(&state_, ninja_command_, options->input_file, argc, argv); + // If we get here, the browse failed. + return 1; +} +#endif // _WIN32 + +#if defined(_MSC_VER) +int NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) { + // Reset getopt: push one argument onto the front of argv, reset optind. + argc++; + argv--; + optind = 0; + return MSVCHelperMain(argc, argv); +} +#endif + +int ToolTargetsList(const vector& nodes, int depth, int indent) { + for (vector::const_iterator n = nodes.begin(); + n != nodes.end(); + ++n) { + for (int i = 0; i < indent; ++i) + printf(" "); + const char* target = (*n)->path().c_str(); + if ((*n)->in_edge()) { + printf("%s: %s\n", target, (*n)->in_edge()->rule_->name().c_str()); + if (depth > 1 || depth <= 0) + ToolTargetsList((*n)->in_edge()->inputs_, depth - 1, indent + 1); + } else { + printf("%s\n", target); + } + } + return 0; +} + +int ToolTargetsSourceList(State* state) { + for (vector::iterator e = state->edges_.begin(); + e != state->edges_.end(); ++e) { + for (vector::iterator inps = (*e)->inputs_.begin(); + inps != (*e)->inputs_.end(); ++inps) { + if (!(*inps)->in_edge()) + printf("%s\n", (*inps)->path().c_str()); + } + } + return 0; +} + +int ToolTargetsList(State* state, const string& rule_name) { + set rules; + + // Gather the outputs. + for (vector::iterator e = state->edges_.begin(); + e != state->edges_.end(); ++e) { + if ((*e)->rule_->name() == rule_name) { + for (vector::iterator out_node = (*e)->outputs_.begin(); + out_node != (*e)->outputs_.end(); ++out_node) { + rules.insert((*out_node)->path()); + } + } + } + + // Print them. + for (set::const_iterator i = rules.begin(); + i != rules.end(); ++i) { + printf("%s\n", (*i).c_str()); + } + + return 0; +} + +int ToolTargetsList(State* state) { + for (vector::iterator e = state->edges_.begin(); + e != state->edges_.end(); ++e) { + for (vector::iterator out_node = (*e)->outputs_.begin(); + out_node != (*e)->outputs_.end(); ++out_node) { + printf("%s: %s\n", + (*out_node)->path().c_str(), + (*e)->rule_->name().c_str()); + } + } + return 0; +} + +int NinjaMain::ToolDeps(const Options* options, int argc, char** argv) { + vector nodes; + if (argc == 0) { + for (vector::const_iterator ni = deps_log_.nodes().begin(); + ni != deps_log_.nodes().end(); ++ni) { + if (deps_log_.IsDepsEntryLiveFor(*ni)) + nodes.push_back(*ni); + } + } else { + string err; + if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { + Error("%s", err.c_str()); + return 1; + } + } + + RealDiskInterface disk_interface; + for (vector::iterator it = nodes.begin(), end = nodes.end(); + it != end; ++it) { + DepsLog::Deps* deps = deps_log_.GetDeps(*it); + if (!deps) { + printf("%s: deps not found\n", (*it)->path().c_str()); + continue; + } + + string err; + TimeStamp mtime = disk_interface.Stat((*it)->path(), &err); + if (mtime == -1) + Error("%s", err.c_str()); // Log and ignore Stat() errors; + printf("%s: #deps %d, deps mtime %d (%s)\n", + (*it)->path().c_str(), deps->node_count, deps->mtime, + (!mtime || mtime > deps->mtime ? "STALE":"VALID")); + for (int i = 0; i < deps->node_count; ++i) + printf(" %s\n", deps->nodes[i]->path().c_str()); + printf("\n"); + } + + return 0; +} + +int NinjaMain::ToolTargets(const Options* options, int argc, char* argv[]) { + int depth = 1; + if (argc >= 1) { + string mode = argv[0]; + if (mode == "rule") { + string rule; + if (argc > 1) + rule = argv[1]; + if (rule.empty()) + return ToolTargetsSourceList(&state_); + else + return ToolTargetsList(&state_, rule); + } else if (mode == "depth") { + if (argc > 1) + depth = atoi(argv[1]); + } else if (mode == "all") { + return ToolTargetsList(&state_); + } else { + const char* suggestion = + SpellcheckString(mode.c_str(), "rule", "depth", "all", NULL); + if (suggestion) { + Error("unknown target tool mode '%s', did you mean '%s'?", + mode.c_str(), suggestion); + } else { + Error("unknown target tool mode '%s'", mode.c_str()); + } + return 1; + } + } + + string err; + vector root_nodes = state_.RootNodes(&err); + if (err.empty()) { + return ToolTargetsList(root_nodes, depth, 0); + } else { + Error("%s", err.c_str()); + return 1; + } +} + +enum PrintCommandMode { PCM_Single, PCM_All }; +void PrintCommands(Edge* edge, set* seen, PrintCommandMode mode) { + if (!edge) + return; + if (!seen->insert(edge).second) + return; + + if (mode == PCM_All) { + for (vector::iterator in = edge->inputs_.begin(); + in != edge->inputs_.end(); ++in) + PrintCommands((*in)->in_edge(), seen, mode); + } + + if (!edge->is_phony()) + puts(edge->EvaluateCommand().c_str()); +} + +int NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) { + // The clean tool uses getopt, and expects argv[0] to contain the name of + // the tool, i.e. "commands". + ++argc; + --argv; + + PrintCommandMode mode = PCM_All; + + optind = 1; + int opt; + while ((opt = getopt(argc, argv, const_cast("hs"))) != -1) { + switch (opt) { + case 's': + mode = PCM_Single; + break; + case 'h': + default: + printf("usage: ninja -t commands [options] [targets]\n" +"\n" +"options:\n" +" -s only print the final command to build [target], not the whole chain\n" + ); + return 1; + } + } + argv += optind; + argc -= optind; + + vector nodes; + string err; + if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { + Error("%s", err.c_str()); + return 1; + } + + set seen; + for (vector::iterator in = nodes.begin(); in != nodes.end(); ++in) + PrintCommands((*in)->in_edge(), &seen, mode); + + return 0; +} + +int NinjaMain::ToolClean(const Options* options, int argc, char* argv[]) { + // The clean tool uses getopt, and expects argv[0] to contain the name of + // the tool, i.e. "clean". + argc++; + argv--; + + bool generator = false; + bool clean_rules = false; + + optind = 1; + int opt; + while ((opt = getopt(argc, argv, const_cast("hgr"))) != -1) { + switch (opt) { + case 'g': + generator = true; + break; + case 'r': + clean_rules = true; + break; + case 'h': + default: + printf("usage: ninja -t clean [options] [targets]\n" +"\n" +"options:\n" +" -g also clean files marked as ninja generator output\n" +" -r interpret targets as a list of rules to clean instead\n" + ); + return 1; + } + } + argv += optind; + argc -= optind; + + if (clean_rules && argc == 0) { + Error("expected a rule to clean"); + return 1; + } + + Cleaner cleaner(&state_, config_); + if (argc >= 1) { + if (clean_rules) + return cleaner.CleanRules(argc, argv); + else + return cleaner.CleanTargets(argc, argv); + } else { + return cleaner.CleanAll(generator); + } +} + +void EncodeJSONString(const char *str) { + while (*str) { + if (*str == '"' || *str == '\\') + putchar('\\'); + putchar(*str); + str++; + } +} + +int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* argv[]) { + bool first = true; + vector cwd; + + do { + cwd.resize(cwd.size() + 1024); + errno = 0; + } while (!getcwd(&cwd[0], cwd.size()) && errno == ERANGE); + if (errno != 0 && errno != ERANGE) { + Error("cannot determine working directory: %s", strerror(errno)); + return 1; + } + + putchar('['); + for (vector::iterator e = state_.edges_.begin(); + e != state_.edges_.end(); ++e) { + if ((*e)->inputs_.empty()) + continue; + for (int i = 0; i != argc; ++i) { + if ((*e)->rule_->name() == argv[i]) { + if (!first) + putchar(','); + + printf("\n {\n \"directory\": \""); + EncodeJSONString(&cwd[0]); + printf("\",\n \"command\": \""); + EncodeJSONString((*e)->EvaluateCommand().c_str()); + printf("\",\n \"file\": \""); + EncodeJSONString((*e)->inputs_[0]->path().c_str()); + printf("\"\n }"); + + first = false; + } + } + } + + puts("\n]"); + return 0; +} + +int NinjaMain::ToolRecompact(const Options* options, int argc, char* argv[]) { + if (!EnsureBuildDirExists()) + return 1; + + if (!OpenBuildLog(/*recompact_only=*/true) || + !OpenDepsLog(/*recompact_only=*/true)) + return 1; + + return 0; +} + +int NinjaMain::ToolUrtle(const Options* options, int argc, char** argv) { + // RLE encoded. + const char* urtle = +" 13 ,3;2!2;\n8 ,;<11!;\n5 `'<10!(2`'2!\n11 ,6;, `\\. `\\9 .,c13$ec,.\n6 " +",2;11!>; `. ,;!2> .e8$2\".2 \"?7$e.\n <:<8!'` 2.3,.2` ,3!' ;,(?7\";2!2'<" +"; `?6$PF ,;,\n2 `'4!8;<3;5! J2$b,`!>;2!:2!`,d?b`!>\n26 `'-;,(<9!> $F3 )3.:!.2 d\"" +"2 ) !>\n30 7`2'<3!- \"=-='5 .2 `2-=\",!>\n25 .ze9$er2 .,cd16$bc.'\n22 .e" +"14$,26$.\n21 z45$c .\n20 J50$c\n20 14$P\"`?34$b\n20 14$ dbc `2\"?22$?7$c" +"\n20 ?18$c.6 4\"8?4\" c8$P\n9 .2,.8 \"20$c.3 ._14 J9$\n .2,2c9$bec,.2 `?" +"21$c.3`4%,3%,3 c8$P\"\n22$c2 2\"?21$bc2,.2` .2,c7$P2\",cb\n23$b bc,.2\"2" +"?14$2F2\"5?2\",J5$P\" ,zd3$\n24$ ?$3?%3 `2\"2?12$bcucd3$P3\"2 2=7$\n23$P" +"\" ,3;<5!>2;,. `4\"6?2\"2 ,9;, `\"?2$\n"; + int count = 0; + for (const char* p = urtle; *p; p++) { + if ('0' <= *p && *p <= '9') { + count = count*10 + *p - '0'; + } else { + for (int i = 0; i < max(count, 1); ++i) + printf("%c", *p); + count = 0; + } + } + return 0; +} + +/// Find the function to execute for \a tool_name and return it via \a func. +/// Returns a Tool, or NULL if Ninja should exit. +const Tool* ChooseTool(const string& tool_name) { + static const Tool kTools[] = { +#if defined(NINJA_HAVE_BROWSE) + { "browse", "browse dependency graph in a web browser", + Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse }, +#endif +#if defined(_MSC_VER) + { "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)", + Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC }, +#endif + { "clean", "clean built files", + Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean }, + { "commands", "list all commands required to rebuild given targets", + Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands }, + { "deps", "show dependencies stored in the deps log", + Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps }, + { "graph", "output graphviz dot file for targets", + Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph }, + { "query", "show inputs/outputs for a path", + Tool::RUN_AFTER_LOGS, &NinjaMain::ToolQuery }, + { "targets", "list targets by their rule or depth in the DAG", + Tool::RUN_AFTER_LOAD, &NinjaMain::ToolTargets }, + { "compdb", "dump JSON compilation database to stdout", + Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCompilationDatabase }, + { "recompact", "recompacts ninja-internal data structures", + Tool::RUN_AFTER_LOAD, &NinjaMain::ToolRecompact }, + { "urtle", NULL, + Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolUrtle }, + { NULL, NULL, Tool::RUN_AFTER_FLAGS, NULL } + }; + + if (tool_name == "list") { + printf("ninja subtools:\n"); + for (const Tool* tool = &kTools[0]; tool->name; ++tool) { + if (tool->desc) + printf("%10s %s\n", tool->name, tool->desc); + } + return NULL; + } + + for (const Tool* tool = &kTools[0]; tool->name; ++tool) { + if (tool->name == tool_name) + return tool; + } + + vector words; + for (const Tool* tool = &kTools[0]; tool->name; ++tool) + words.push_back(tool->name); + const char* suggestion = SpellcheckStringV(tool_name, words); + if (suggestion) { + Fatal("unknown tool '%s', did you mean '%s'?", + tool_name.c_str(), suggestion); + } else { + Fatal("unknown tool '%s'", tool_name.c_str()); + } + return NULL; // Not reached. +} + +/// Enable a debugging mode. Returns false if Ninja should exit instead +/// of continuing. +bool DebugEnable(const string& name) { + if (name == "list") { + printf("debugging modes:\n" +" stats print operation counts/timing info\n" +" explain explain what caused a command to execute\n" +" keepdepfile don't delete depfiles after they're read by ninja\n" +" keeprsp don't delete @response files on success\n" +#ifdef _WIN32 +" nostatcache don't batch stat() calls per directory and cache them\n" +#endif +"multiple modes can be enabled via -d FOO -d BAR\n"); + return false; + } else if (name == "stats") { + g_metrics = new Metrics; + return true; + } else if (name == "explain") { + g_explaining = true; + return true; + } else if (name == "keepdepfile") { + g_keep_depfile = true; + return true; + } else if (name == "keeprsp") { + g_keep_rsp = true; + return true; + } else if (name == "nostatcache") { + g_experimental_statcache = false; + return true; + } else { + const char* suggestion = + SpellcheckString(name.c_str(), + "stats", "explain", "keepdepfile", "keeprsp", + "nostatcache", NULL); + if (suggestion) { + Error("unknown debug setting '%s', did you mean '%s'?", + name.c_str(), suggestion); + } else { + Error("unknown debug setting '%s'", name.c_str()); + } + return false; + } +} + +/// Set a warning flag. Returns false if Ninja should exit instead of +/// continuing. +bool WarningEnable(const string& name, Options* options) { + if (name == "list") { + printf("warning flags:\n" +" dupbuild={err,warn} multiple build lines for one target\n" +" phonycycle={err,warn} phony build statement references itself\n"); + return false; + } else if (name == "dupbuild=err") { + options->dupe_edges_should_err = true; + return true; + } else if (name == "dupbuild=warn") { + options->dupe_edges_should_err = false; + return true; + } else if (name == "phonycycle=err") { + options->phony_cycle_should_err = true; + return true; + } else if (name == "phonycycle=warn") { + options->phony_cycle_should_err = false; + return true; + } else { + const char* suggestion = + SpellcheckString(name.c_str(), "dupbuild=err", "dupbuild=warn", + "phonycycle=err", "phonycycle=warn", NULL); + if (suggestion) { + Error("unknown warning flag '%s', did you mean '%s'?", + name.c_str(), suggestion); + } else { + Error("unknown warning flag '%s'", name.c_str()); + } + return false; + } +} + +bool NinjaMain::OpenBuildLog(bool recompact_only) { + string log_path = ".ninja_log"; + if (!build_dir_.empty()) + log_path = build_dir_ + "/" + log_path; + + string err; + if (!build_log_.Load(log_path, &err)) { + Error("loading build log %s: %s", log_path.c_str(), err.c_str()); + return false; + } + if (!err.empty()) { + // Hack: Load() can return a warning via err by returning true. + Warning("%s", err.c_str()); + err.clear(); + } + + if (recompact_only) { + bool success = build_log_.Recompact(log_path, *this, &err); + if (!success) + Error("failed recompaction: %s", err.c_str()); + return success; + } + + if (!config_.dry_run) { + if (!build_log_.OpenForWrite(log_path, *this, &err)) { + Error("opening build log: %s", err.c_str()); + return false; + } + } + + return true; +} + +/// Open the deps log: load it, then open for writing. +/// @return false on error. +bool NinjaMain::OpenDepsLog(bool recompact_only) { + string path = ".ninja_deps"; + if (!build_dir_.empty()) + path = build_dir_ + "/" + path; + + string err; + if (!deps_log_.Load(path, &state_, &err)) { + Error("loading deps log %s: %s", path.c_str(), err.c_str()); + return false; + } + if (!err.empty()) { + // Hack: Load() can return a warning via err by returning true. + Warning("%s", err.c_str()); + err.clear(); + } + + if (recompact_only) { + bool success = deps_log_.Recompact(path, &err); + if (!success) + Error("failed recompaction: %s", err.c_str()); + return success; + } + + if (!config_.dry_run) { + if (!deps_log_.OpenForWrite(path, &err)) { + Error("opening deps log: %s", err.c_str()); + return false; + } + } + + return true; +} + +void NinjaMain::DumpMetrics() { + g_metrics->Report(); + + printf("\n"); + int count = (int)state_.paths_.size(); + int buckets = (int)state_.paths_.bucket_count(); + printf("path->node hash load %.2f (%d entries / %d buckets)\n", + count / (double) buckets, count, buckets); +} + +bool NinjaMain::EnsureBuildDirExists() { + build_dir_ = state_.bindings_.LookupVariable("builddir"); + if (!build_dir_.empty() && !config_.dry_run) { + if (!disk_interface_.MakeDirs(build_dir_ + "/.") && errno != EEXIST) { + Error("creating build directory %s: %s", + build_dir_.c_str(), strerror(errno)); + return false; + } + } + return true; +} + +int NinjaMain::RunBuild(int argc, char** argv) { + string err; + vector targets; + if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) { + Error("%s", err.c_str()); + return 1; + } + + disk_interface_.AllowStatCache(g_experimental_statcache); + + Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_); + for (size_t i = 0; i < targets.size(); ++i) { + if (!builder.AddTarget(targets[i], &err)) { + if (!err.empty()) { + Error("%s", err.c_str()); + return 1; + } else { + // Added a target that is already up-to-date; not really + // an error. + } + } + } + + // Make sure restat rules do not see stale timestamps. + disk_interface_.AllowStatCache(false); + + if (builder.AlreadyUpToDate()) { + printf("ninja: no work to do.\n"); + return 0; + } + + if (!builder.Build(&err)) { + printf("ninja: build stopped: %s.\n", err.c_str()); + if (err.find("interrupted by user") != string::npos) { + return 2; + } + return 1; + } + + return 0; +} + +#ifdef _MSC_VER + +/// This handler processes fatal crashes that you can't catch +/// Test example: C++ exception in a stack-unwind-block +/// Real-world example: ninja launched a compiler to process a tricky +/// C++ input file. The compiler got itself into a state where it +/// generated 3 GB of output and caused ninja to crash. +void TerminateHandler() { + CreateWin32MiniDump(NULL); + Fatal("terminate handler called"); +} + +/// On Windows, we want to prevent error dialogs in case of exceptions. +/// This function handles the exception, and writes a minidump. +int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) { + Error("exception: 0x%X", code); // e.g. EXCEPTION_ACCESS_VIOLATION + fflush(stderr); + CreateWin32MiniDump(ep); + return EXCEPTION_EXECUTE_HANDLER; +} + +#endif // _MSC_VER + +/// Parse argv for command-line options. +/// Returns an exit code, or -1 if Ninja should continue. +int ReadFlags(int* argc, char*** argv, + Options* options, BuildConfig* config) { + config->parallelism = GuessParallelism(); + + enum { OPT_VERSION = 1 }; + const option kLongOptions[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, OPT_VERSION }, + { NULL, 0, NULL, 0 } + }; + + int opt; + while (!options->tool && + (opt = getopt_long(*argc, *argv, "d:f:j:k:l:nt:vw:C:h", kLongOptions, + NULL)) != -1) { + switch (opt) { + case 'd': + if (!DebugEnable(optarg)) + return 1; + break; + case 'f': + options->input_file = optarg; + break; + case 'j': { + char* end; + int value = strtol(optarg, &end, 10); + if (*end != 0 || value <= 0) + Fatal("invalid -j parameter"); + config->parallelism = value; + break; + } + case 'k': { + char* end; + int value = strtol(optarg, &end, 10); + if (*end != 0) + Fatal("-k parameter not numeric; did you mean -k 0?"); + + // We want to go until N jobs fail, which means we should allow + // N failures and then stop. For N <= 0, INT_MAX is close enough + // to infinite for most sane builds. + config->failures_allowed = value > 0 ? value : INT_MAX; + break; + } + case 'l': { + char* end; + double value = strtod(optarg, &end); + if (end == optarg) + Fatal("-l parameter not numeric: did you mean -l 0.0?"); + config->max_load_average = value; + break; + } + case 'n': + config->dry_run = true; + break; + case 't': + options->tool = ChooseTool(optarg); + if (!options->tool) + return 0; + break; + case 'v': + config->verbosity = BuildConfig::VERBOSE; + break; + case 'w': + if (!WarningEnable(optarg, options)) + return 1; + break; + case 'C': + options->working_dir = optarg; + break; + case OPT_VERSION: + printf("%s\n", kNinjaVersion); + return 0; + case 'h': + default: + Usage(*config); + return 1; + } + } + *argv += optind; + *argc -= optind; + + return -1; +} + +int real_main(int argc, char** argv) { + BuildConfig config; + Options options = {}; + options.input_file = "build.ninja"; + + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); + const char* ninja_command = argv[0]; + + int exit_code = ReadFlags(&argc, &argv, &options, &config); + if (exit_code >= 0) + return exit_code; + + if (options.working_dir) { + // The formatting of this string, complete with funny quotes, is + // so Emacs can properly identify that the cwd has changed for + // subsequent commands. + // Don't print this if a tool is being used, so that tool output + // can be piped into a file without this string showing up. + if (!options.tool) + printf("ninja: Entering directory `%s'\n", options.working_dir); + if (chdir(options.working_dir) < 0) { + Fatal("chdir to '%s' - %s", options.working_dir, strerror(errno)); + } + } + + if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) { + // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed + // by other tools. + NinjaMain ninja(ninja_command, config); + return (ninja.*options.tool->func)(&options, argc, argv); + } + + // Limit number of rebuilds, to prevent infinite loops. + const int kCycleLimit = 100; + for (int cycle = 1; cycle <= kCycleLimit; ++cycle) { + NinjaMain ninja(ninja_command, config); + + ManifestParserOptions parser_opts; + if (options.dupe_edges_should_err) { + parser_opts.dupe_edge_action_ = kDupeEdgeActionError; + } + if (options.phony_cycle_should_err) { + parser_opts.phony_cycle_action_ = kPhonyCycleActionError; + } + ManifestParser parser(&ninja.state_, &ninja.disk_interface_, parser_opts); + string err; + if (!parser.Load(options.input_file, &err)) { + Error("%s", err.c_str()); + return 1; + } + + if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD) + return (ninja.*options.tool->func)(&options, argc, argv); + + if (!ninja.EnsureBuildDirExists()) + return 1; + + if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog()) + return 1; + + if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS) + return (ninja.*options.tool->func)(&options, argc, argv); + + // Attempt to rebuild the manifest before building anything else + if (ninja.RebuildManifest(options.input_file, &err)) { + // In dry_run mode the regeneration will succeed without changing the + // manifest forever. Better to return immediately. + if (config.dry_run) + return 0; + // Start the build over with the new manifest. + continue; + } else if (!err.empty()) { + Error("rebuilding '%s': %s", options.input_file, err.c_str()); + return 1; + } + + int result = ninja.RunBuild(argc, argv); + if (g_metrics) + ninja.DumpMetrics(); + return result; + } + + Error("manifest '%s' still dirty after %d tries\n", + options.input_file, kCycleLimit); + return 1; +} + +} // anonymous namespace + +int main(int argc, char** argv) { +#if defined(_MSC_VER) + // Set a handler to catch crashes not caught by the __try..__except + // block (e.g. an exception in a stack-unwind-block). + std::set_terminate(TerminateHandler); + __try { + // Running inside __try ... __except suppresses any Windows error + // dialogs for errors such as bad_alloc. + return real_main(argc, argv); + } + __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { + // Common error situations return exitCode=1. 2 was chosen to + // indicate a more serious problem. + return 2; + } +#else + return real_main(argc, argv); +#endif +} diff --git a/src/3rdparty/ninja/src/ninja_test.cc b/src/3rdparty/ninja/src/ninja_test.cc new file mode 100644 index 00000000000..d642c5c90d2 --- /dev/null +++ b/src/3rdparty/ninja/src/ninja_test.cc @@ -0,0 +1,160 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#ifdef _WIN32 +#include "getopt.h" +#elif defined(_AIX) +#include "getopt.h" +#include +#else +#include +#endif + +#include "test.h" +#include "line_printer.h" + +struct RegisteredTest { + testing::Test* (*factory)(); + const char *name; + bool should_run; +}; +// This can't be a vector because tests call RegisterTest from static +// initializers and the order static initializers run it isn't specified. So +// the vector constructor isn't guaranteed to run before all of the +// RegisterTest() calls. +static RegisteredTest tests[10000]; +testing::Test* g_current_test; +static int ntests; +static LinePrinter printer; + +void RegisterTest(testing::Test* (*factory)(), const char* name) { + tests[ntests].factory = factory; + tests[ntests++].name = name; +} + +namespace { +string StringPrintf(const char* format, ...) { + const int N = 1024; + char buf[N]; + + va_list ap; + va_start(ap, format); + vsnprintf(buf, N, format, ap); + va_end(ap); + + return buf; +} + +void Usage() { + fprintf(stderr, +"usage: ninja_tests [options]\n" +"\n" +"options:\n" +" --gtest_filter=POSTIVE_PATTERN[-NEGATIVE_PATTERN]\n" +" Run tests whose names match the positive but not the negative pattern.\n" +" '*' matches any substring. (gtest's ':', '?' are not implemented).\n"); +} + +bool PatternMatchesString(const char* pattern, const char* str) { + switch (*pattern) { + case '\0': + case '-': return *str == '\0'; + case '*': return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool TestMatchesFilter(const char* test, const char* filter) { + // Split --gtest_filter at '-' into positive and negative filters. + const char* const dash = strchr(filter, '-'); + const char* pos = dash == filter ? "*" : filter; //Treat '-test1' as '*-test1' + const char* neg = dash ? dash + 1 : ""; + return PatternMatchesString(pos, test) && !PatternMatchesString(neg, test); +} + +bool ReadFlags(int* argc, char*** argv, const char** test_filter) { + enum { OPT_GTEST_FILTER = 1 }; + const option kLongOptions[] = { + { "gtest_filter", required_argument, NULL, OPT_GTEST_FILTER }, + { NULL, 0, NULL, 0 } + }; + + int opt; + while ((opt = getopt_long(*argc, *argv, "h", kLongOptions, NULL)) != -1) { + switch (opt) { + case OPT_GTEST_FILTER: + if (strchr(optarg, '?') == NULL && strchr(optarg, ':') == NULL) { + *test_filter = optarg; + break; + } // else fall through. + default: + Usage(); + return false; + } + } + *argv += optind; + *argc -= optind; + return true; +} + +} // namespace + +bool testing::Test::Check(bool condition, const char* file, int line, + const char* error) { + if (!condition) { + printer.PrintOnNewLine( + StringPrintf("*** Failure in %s:%d\n%s\n", file, line, error)); + failed_ = true; + } + return condition; +} + +int main(int argc, char **argv) { + int tests_started = 0; + + const char* test_filter = "*"; + if (!ReadFlags(&argc, &argv, &test_filter)) + return 1; + + int nactivetests = 0; + for (int i = 0; i < ntests; i++) + if ((tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter))) + ++nactivetests; + + bool passed = true; + for (int i = 0; i < ntests; i++) { + if (!tests[i].should_run) continue; + + ++tests_started; + testing::Test* test = tests[i].factory(); + printer.Print( + StringPrintf("[%d/%d] %s", tests_started, nactivetests, tests[i].name), + LinePrinter::ELIDE); + test->SetUp(); + test->Run(); + test->TearDown(); + if (test->Failed()) + passed = false; + delete test; + } + + printer.PrintOnNewLine(passed ? "passed\n" : "failed\n"); + return passed ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/3rdparty/ninja/src/state.cc b/src/3rdparty/ninja/src/state.cc new file mode 100644 index 00000000000..9b3c7e1983a --- /dev/null +++ b/src/3rdparty/ninja/src/state.cc @@ -0,0 +1,212 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "state.h" + +#include +#include + +#include "edit_distance.h" +#include "graph.h" +#include "metrics.h" +#include "util.h" + + +void Pool::EdgeScheduled(const Edge& edge) { + if (depth_ != 0) + current_use_ += edge.weight(); +} + +void Pool::EdgeFinished(const Edge& edge) { + if (depth_ != 0) + current_use_ -= edge.weight(); +} + +void Pool::DelayEdge(Edge* edge) { + assert(depth_ != 0); + delayed_.insert(edge); +} + +void Pool::RetrieveReadyEdges(set* ready_queue) { + DelayedEdges::iterator it = delayed_.begin(); + while (it != delayed_.end()) { + Edge* edge = *it; + if (current_use_ + edge->weight() > depth_) + break; + ready_queue->insert(edge); + EdgeScheduled(*edge); + ++it; + } + delayed_.erase(delayed_.begin(), it); +} + +void Pool::Dump() const { + printf("%s (%d/%d) ->\n", name_.c_str(), current_use_, depth_); + for (DelayedEdges::const_iterator it = delayed_.begin(); + it != delayed_.end(); ++it) + { + printf("\t"); + (*it)->Dump(); + } +} + +// static +bool Pool::WeightedEdgeCmp(const Edge* a, const Edge* b) { + if (!a) return b; + if (!b) return false; + int weight_diff = a->weight() - b->weight(); + return ((weight_diff < 0) || (weight_diff == 0 && a < b)); +} + +Pool State::kDefaultPool("", 0); +Pool State::kConsolePool("console", 1); +const Rule State::kPhonyRule("phony"); + +State::State() { + bindings_.AddRule(&kPhonyRule); + AddPool(&kDefaultPool); + AddPool(&kConsolePool); +} + +void State::AddPool(Pool* pool) { + assert(LookupPool(pool->name()) == NULL); + pools_[pool->name()] = pool; +} + +Pool* State::LookupPool(const string& pool_name) { + map::iterator i = pools_.find(pool_name); + if (i == pools_.end()) + return NULL; + return i->second; +} + +Edge* State::AddEdge(const Rule* rule) { + Edge* edge = new Edge(); + edge->rule_ = rule; + edge->pool_ = &State::kDefaultPool; + edge->env_ = &bindings_; + edges_.push_back(edge); + return edge; +} + +Node* State::GetNode(StringPiece path, uint64_t slash_bits) { + Node* node = LookupNode(path); + if (node) + return node; + node = new Node(path.AsString(), slash_bits); + paths_[node->path()] = node; + return node; +} + +Node* State::LookupNode(StringPiece path) const { + METRIC_RECORD("lookup node"); + Paths::const_iterator i = paths_.find(path); + if (i != paths_.end()) + return i->second; + return NULL; +} + +Node* State::SpellcheckNode(const string& path) { + const bool kAllowReplacements = true; + const int kMaxValidEditDistance = 3; + + int min_distance = kMaxValidEditDistance + 1; + Node* result = NULL; + for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) { + int distance = EditDistance( + i->first, path, kAllowReplacements, kMaxValidEditDistance); + if (distance < min_distance && i->second) { + min_distance = distance; + result = i->second; + } + } + return result; +} + +void State::AddIn(Edge* edge, StringPiece path, uint64_t slash_bits) { + Node* node = GetNode(path, slash_bits); + edge->inputs_.push_back(node); + node->AddOutEdge(edge); +} + +bool State::AddOut(Edge* edge, StringPiece path, uint64_t slash_bits) { + Node* node = GetNode(path, slash_bits); + if (node->in_edge()) + return false; + edge->outputs_.push_back(node); + node->set_in_edge(edge); + return true; +} + +bool State::AddDefault(StringPiece path, string* err) { + Node* node = LookupNode(path); + if (!node) { + *err = "unknown target '" + path.AsString() + "'"; + return false; + } + defaults_.push_back(node); + return true; +} + +vector State::RootNodes(string* err) const { + vector root_nodes; + // Search for nodes with no output. + for (vector::const_iterator e = edges_.begin(); + e != edges_.end(); ++e) { + for (vector::const_iterator out = (*e)->outputs_.begin(); + out != (*e)->outputs_.end(); ++out) { + if ((*out)->out_edges().empty()) + root_nodes.push_back(*out); + } + } + + if (!edges_.empty() && root_nodes.empty()) + *err = "could not determine root nodes of build graph"; + + return root_nodes; +} + +vector State::DefaultNodes(string* err) const { + return defaults_.empty() ? RootNodes(err) : defaults_; +} + +void State::Reset() { + for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) + i->second->ResetState(); + for (vector::iterator e = edges_.begin(); e != edges_.end(); ++e) { + (*e)->outputs_ready_ = false; + (*e)->mark_ = Edge::VisitNone; + } +} + +void State::Dump() { + for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) { + Node* node = i->second; + printf("%s %s [id:%d]\n", + node->path().c_str(), + node->status_known() ? (node->dirty() ? "dirty" : "clean") + : "unknown", + node->id()); + } + if (!pools_.empty()) { + printf("resource_pools:\n"); + for (map::const_iterator it = pools_.begin(); + it != pools_.end(); ++it) + { + if (!it->second->name().empty()) { + it->second->Dump(); + } + } + } +} diff --git a/src/3rdparty/ninja/src/state.h b/src/3rdparty/ninja/src/state.h new file mode 100644 index 00000000000..54e9dc54a7d --- /dev/null +++ b/src/3rdparty/ninja/src/state.h @@ -0,0 +1,131 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_STATE_H_ +#define NINJA_STATE_H_ + +#include +#include +#include +#include +using namespace std; + +#include "eval_env.h" +#include "hash_map.h" +#include "util.h" + +struct Edge; +struct Node; +struct Rule; + +/// A pool for delayed edges. +/// Pools are scoped to a State. Edges within a State will share Pools. A Pool +/// will keep a count of the total 'weight' of the currently scheduled edges. If +/// a Plan attempts to schedule an Edge which would cause the total weight to +/// exceed the depth of the Pool, the Pool will enque the Edge instead of +/// allowing the Plan to schedule it. The Pool will relinquish queued Edges when +/// the total scheduled weight diminishes enough (i.e. when a scheduled edge +/// completes). +struct Pool { + Pool(const string& name, int depth) + : name_(name), current_use_(0), depth_(depth), delayed_(&WeightedEdgeCmp) {} + + // A depth of 0 is infinite + bool is_valid() const { return depth_ >= 0; } + int depth() const { return depth_; } + const string& name() const { return name_; } + int current_use() const { return current_use_; } + + /// true if the Pool might delay this edge + bool ShouldDelayEdge() const { return depth_ != 0; } + + /// informs this Pool that the given edge is committed to be run. + /// Pool will count this edge as using resources from this pool. + void EdgeScheduled(const Edge& edge); + + /// informs this Pool that the given edge is no longer runnable, and should + /// relinquish its resources back to the pool + void EdgeFinished(const Edge& edge); + + /// adds the given edge to this Pool to be delayed. + void DelayEdge(Edge* edge); + + /// Pool will add zero or more edges to the ready_queue + void RetrieveReadyEdges(set* ready_queue); + + /// Dump the Pool and its edges (useful for debugging). + void Dump() const; + + private: + string name_; + + /// |current_use_| is the total of the weights of the edges which are + /// currently scheduled in the Plan (i.e. the edges in Plan::ready_). + int current_use_; + int depth_; + + static bool WeightedEdgeCmp(const Edge* a, const Edge* b); + + typedef set DelayedEdges; + DelayedEdges delayed_; +}; + +/// Global state (file status) for a single run. +struct State { + static Pool kDefaultPool; + static Pool kConsolePool; + static const Rule kPhonyRule; + + State(); + + void AddPool(Pool* pool); + Pool* LookupPool(const string& pool_name); + + Edge* AddEdge(const Rule* rule); + + Node* GetNode(StringPiece path, uint64_t slash_bits); + Node* LookupNode(StringPiece path) const; + Node* SpellcheckNode(const string& path); + + void AddIn(Edge* edge, StringPiece path, uint64_t slash_bits); + bool AddOut(Edge* edge, StringPiece path, uint64_t slash_bits); + bool AddDefault(StringPiece path, string* error); + + /// Reset state. Keeps all nodes and edges, but restores them to the + /// state where we haven't yet examined the disk for dirty state. + void Reset(); + + /// Dump the nodes and Pools (useful for debugging). + void Dump(); + + /// @return the root node(s) of the graph. (Root nodes have no output edges). + /// @param error where to write the error message if somethings went wrong. + vector RootNodes(string* error) const; + vector DefaultNodes(string* error) const; + + /// Mapping of path -> Node. + typedef ExternalStringHashMap::Type Paths; + Paths paths_; + + /// All the pools used in the graph. + map pools_; + + /// All the edges of the graph. + vector edges_; + + BindingEnv bindings_; + vector defaults_; +}; + +#endif // NINJA_STATE_H_ diff --git a/src/3rdparty/ninja/src/state_test.cc b/src/3rdparty/ninja/src/state_test.cc new file mode 100644 index 00000000000..458b5196c34 --- /dev/null +++ b/src/3rdparty/ninja/src/state_test.cc @@ -0,0 +1,46 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "graph.h" +#include "state.h" +#include "test.h" + +namespace { + +TEST(State, Basic) { + State state; + + EvalString command; + command.AddText("cat "); + command.AddSpecial("in"); + command.AddText(" > "); + command.AddSpecial("out"); + + Rule* rule = new Rule("cat"); + rule->AddBinding("command", command); + state.bindings_.AddRule(rule); + + Edge* edge = state.AddEdge(rule); + state.AddIn(edge, "in1", 0); + state.AddIn(edge, "in2", 0); + state.AddOut(edge, "out", 0); + + EXPECT_EQ("cat in1 in2 > out", edge->EvaluateCommand()); + + EXPECT_FALSE(state.GetNode("in1", 0)->dirty()); + EXPECT_FALSE(state.GetNode("in2", 0)->dirty()); + EXPECT_FALSE(state.GetNode("out", 0)->dirty()); +} + +} // namespace diff --git a/src/3rdparty/ninja/src/string_piece.h b/src/3rdparty/ninja/src/string_piece.h new file mode 100644 index 00000000000..031bda48d3f --- /dev/null +++ b/src/3rdparty/ninja/src/string_piece.h @@ -0,0 +1,71 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_STRINGPIECE_H_ +#define NINJA_STRINGPIECE_H_ + +#include + +using namespace std; + +#include + +/// StringPiece represents a slice of a string whose memory is managed +/// externally. It is useful for reducing the number of std::strings +/// we need to allocate. +struct StringPiece { + typedef const char* const_iterator; + + StringPiece() : str_(NULL), len_(0) {} + + /// The constructors intentionally allow for implicit conversions. + StringPiece(const string& str) : str_(str.data()), len_(str.size()) {} + StringPiece(const char* str) : str_(str), len_(strlen(str)) {} + + StringPiece(const char* str, size_t len) : str_(str), len_(len) {} + + bool operator==(const StringPiece& other) const { + return len_ == other.len_ && memcmp(str_, other.str_, len_) == 0; + } + bool operator!=(const StringPiece& other) const { + return !(*this == other); + } + + /// Convert the slice into a full-fledged std::string, copying the + /// data into a new string. + string AsString() const { + return len_ ? string(str_, len_) : string(); + } + + const_iterator begin() const { + return str_; + } + + const_iterator end() const { + return str_ + len_; + } + + char operator[](size_t pos) const { + return str_[pos]; + } + + size_t size() const { + return len_; + } + + const char* str_; + size_t len_; +}; + +#endif // NINJA_STRINGPIECE_H_ diff --git a/src/3rdparty/ninja/src/string_piece_util.cc b/src/3rdparty/ninja/src/string_piece_util.cc new file mode 100644 index 00000000000..8e1ecfddd73 --- /dev/null +++ b/src/3rdparty/ninja/src/string_piece_util.cc @@ -0,0 +1,78 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "string_piece_util.h" + +#include +#include +#include +using namespace std; + +vector SplitStringPiece(StringPiece input, char sep) { + vector elems; + elems.reserve(count(input.begin(), input.end(), sep) + 1); + + StringPiece::const_iterator pos = input.begin(); + + for (;;) { + const char* next_pos = find(pos, input.end(), sep); + if (next_pos == input.end()) { + elems.push_back(StringPiece(pos, input.end() - pos)); + break; + } + elems.push_back(StringPiece(pos, next_pos - pos)); + pos = next_pos + 1; + } + + return elems; +} + +string JoinStringPiece(const vector& list, char sep) { + if (list.size() == 0){ + return ""; + } + + string ret; + + { + size_t cap = list.size() - 1; + for (size_t i = 0; i < list.size(); ++i) { + cap += list[i].len_; + } + ret.reserve(cap); + } + + for (size_t i = 0; i < list.size(); ++i) { + if (i != 0) { + ret += sep; + } + ret.append(list[i].str_, list[i].len_); + } + + return ret; +} + +bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) { + if (a.len_ != b.len_) { + return false; + } + + for (size_t i = 0; i < a.len_; ++i) { + if (ToLowerASCII(a.str_[i]) != ToLowerASCII(b.str_[i])) { + return false; + } + } + + return true; +} diff --git a/src/3rdparty/ninja/src/string_piece_util.h b/src/3rdparty/ninja/src/string_piece_util.h new file mode 100644 index 00000000000..2e40b9f3b28 --- /dev/null +++ b/src/3rdparty/ninja/src/string_piece_util.h @@ -0,0 +1,34 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_STRINGPIECE_UTIL_H_ +#define NINJA_STRINGPIECE_UTIL_H_ + +#include +#include + +#include "string_piece.h" +using namespace std; + +vector SplitStringPiece(StringPiece input, char sep); + +string JoinStringPiece(const vector& list, char sep); + +inline char ToLowerASCII(char c) { + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} + +bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b); + +#endif // NINJA_STRINGPIECE_UTIL_H_ diff --git a/src/3rdparty/ninja/src/string_piece_util_test.cc b/src/3rdparty/ninja/src/string_piece_util_test.cc new file mode 100644 index 00000000000..648c6479180 --- /dev/null +++ b/src/3rdparty/ninja/src/string_piece_util_test.cc @@ -0,0 +1,129 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "string_piece_util.h" + +#include "test.h" + +TEST(StringPieceUtilTest, SplitStringPiece) { + { + string input("a:b:c"); + vector list = SplitStringPiece(input, ':'); + + EXPECT_EQ(list.size(), 3); + + EXPECT_EQ(list[0], "a"); + EXPECT_EQ(list[1], "b"); + EXPECT_EQ(list[2], "c"); + } + + { + string empty(""); + vector list = SplitStringPiece(empty, ':'); + + EXPECT_EQ(list.size(), 1); + + EXPECT_EQ(list[0], ""); + } + + { + string one("a"); + vector list = SplitStringPiece(one, ':'); + + EXPECT_EQ(list.size(), 1); + + EXPECT_EQ(list[0], "a"); + } + + { + string sep_only(":"); + vector list = SplitStringPiece(sep_only, ':'); + + EXPECT_EQ(list.size(), 2); + + EXPECT_EQ(list[0], ""); + EXPECT_EQ(list[1], ""); + } + + { + string sep(":a:b:c:"); + vector list = SplitStringPiece(sep, ':'); + + EXPECT_EQ(list.size(), 5); + + EXPECT_EQ(list[0], ""); + EXPECT_EQ(list[1], "a"); + EXPECT_EQ(list[2], "b"); + EXPECT_EQ(list[3], "c"); + EXPECT_EQ(list[4], ""); + } +} + +TEST(StringPieceUtilTest, JoinStringPiece) { + { + string input("a:b:c"); + vector list = SplitStringPiece(input, ':'); + + EXPECT_EQ("a:b:c", JoinStringPiece(list, ':')); + EXPECT_EQ("a/b/c", JoinStringPiece(list, '/')); + } + + { + string empty(""); + vector list = SplitStringPiece(empty, ':'); + + EXPECT_EQ("", JoinStringPiece(list, ':')); + } + + { + vector empty_list; + + EXPECT_EQ("", JoinStringPiece(empty_list, ':')); + } + + { + string one("a"); + vector single_list = SplitStringPiece(one, ':'); + + EXPECT_EQ("a", JoinStringPiece(single_list, ':')); + } + + { + string sep(":a:b:c:"); + vector list = SplitStringPiece(sep, ':'); + + EXPECT_EQ(":a:b:c:", JoinStringPiece(list, ':')); + } +} + +TEST(StringPieceUtilTest, ToLowerASCII) { + EXPECT_EQ('a', ToLowerASCII('A')); + EXPECT_EQ('z', ToLowerASCII('Z')); + EXPECT_EQ('a', ToLowerASCII('a')); + EXPECT_EQ('z', ToLowerASCII('z')); + EXPECT_EQ('/', ToLowerASCII('/')); + EXPECT_EQ('1', ToLowerASCII('1')); +} + +TEST(StringPieceUtilTest, EqualsCaseInsensitiveASCII) { + EXPECT_TRUE(EqualsCaseInsensitiveASCII("abc", "abc")); + EXPECT_TRUE(EqualsCaseInsensitiveASCII("abc", "ABC")); + EXPECT_TRUE(EqualsCaseInsensitiveASCII("abc", "aBc")); + EXPECT_TRUE(EqualsCaseInsensitiveASCII("AbC", "aBc")); + EXPECT_TRUE(EqualsCaseInsensitiveASCII("", "")); + + EXPECT_FALSE(EqualsCaseInsensitiveASCII("a", "ac")); + EXPECT_FALSE(EqualsCaseInsensitiveASCII("/", "\\")); + EXPECT_FALSE(EqualsCaseInsensitiveASCII("1", "10")); +} diff --git a/src/3rdparty/ninja/src/subprocess-posix.cc b/src/3rdparty/ninja/src/subprocess-posix.cc new file mode 100644 index 00000000000..1de22c38f7f --- /dev/null +++ b/src/3rdparty/ninja/src/subprocess-posix.cc @@ -0,0 +1,338 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "subprocess.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char** environ; + +#include "util.h" + +Subprocess::Subprocess(bool use_console) : fd_(-1), pid_(-1), + use_console_(use_console) { +} + +Subprocess::~Subprocess() { + if (fd_ >= 0) + close(fd_); + // Reap child if forgotten. + if (pid_ != -1) + Finish(); +} + +bool Subprocess::Start(SubprocessSet* set, const string& command) { + int output_pipe[2]; + if (pipe(output_pipe) < 0) + Fatal("pipe: %s", strerror(errno)); + fd_ = output_pipe[0]; +#if !defined(USE_PPOLL) + // If available, we use ppoll in DoWork(); otherwise we use pselect + // and so must avoid overly-large FDs. + if (fd_ >= static_cast(FD_SETSIZE)) + Fatal("pipe: %s", strerror(EMFILE)); +#endif // !USE_PPOLL + SetCloseOnExec(fd_); + + posix_spawn_file_actions_t action; + if (posix_spawn_file_actions_init(&action) != 0) + Fatal("posix_spawn_file_actions_init: %s", strerror(errno)); + + if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0) + Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno)); + + posix_spawnattr_t attr; + if (posix_spawnattr_init(&attr) != 0) + Fatal("posix_spawnattr_init: %s", strerror(errno)); + + short flags = 0; + + flags |= POSIX_SPAWN_SETSIGMASK; + if (posix_spawnattr_setsigmask(&attr, &set->old_mask_) != 0) + Fatal("posix_spawnattr_setsigmask: %s", strerror(errno)); + // Signals which are set to be caught in the calling process image are set to + // default action in the new process image, so no explicit + // POSIX_SPAWN_SETSIGDEF parameter is needed. + + if (!use_console_) { + // Put the child in its own process group, so ctrl-c won't reach it. + flags |= POSIX_SPAWN_SETPGROUP; + // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default. + + // Open /dev/null over stdin. + if (posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY, + 0) != 0) { + Fatal("posix_spawn_file_actions_addopen: %s", strerror(errno)); + } + + if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1) != 0) + Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); + if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2) != 0) + Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); + if (posix_spawn_file_actions_addclose(&action, output_pipe[1]) != 0) + Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno)); + // In the console case, output_pipe is still inherited by the child and + // closed when the subprocess finishes, which then notifies ninja. + } +#ifdef POSIX_SPAWN_USEVFORK + flags |= POSIX_SPAWN_USEVFORK; +#endif + + if (posix_spawnattr_setflags(&attr, flags) != 0) + Fatal("posix_spawnattr_setflags: %s", strerror(errno)); + + const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL }; + if (posix_spawn(&pid_, "/bin/sh", &action, &attr, + const_cast(spawned_args), environ) != 0) + Fatal("posix_spawn: %s", strerror(errno)); + + if (posix_spawnattr_destroy(&attr) != 0) + Fatal("posix_spawnattr_destroy: %s", strerror(errno)); + if (posix_spawn_file_actions_destroy(&action) != 0) + Fatal("posix_spawn_file_actions_destroy: %s", strerror(errno)); + + close(output_pipe[1]); + return true; +} + +void Subprocess::OnPipeReady() { + char buf[4 << 10]; + ssize_t len = read(fd_, buf, sizeof(buf)); + if (len > 0) { + buf_.append(buf, len); + } else { + if (len < 0) + Fatal("read: %s", strerror(errno)); + close(fd_); + fd_ = -1; + } +} + +ExitStatus Subprocess::Finish() { + assert(pid_ != -1); + int status; + if (waitpid(pid_, &status, 0) < 0) + Fatal("waitpid(%d): %s", pid_, strerror(errno)); + pid_ = -1; + + if (WIFEXITED(status)) { + int exit = WEXITSTATUS(status); + if (exit == 0) + return ExitSuccess; + } else if (WIFSIGNALED(status)) { + if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM + || WTERMSIG(status) == SIGHUP) + return ExitInterrupted; + } + return ExitFailure; +} + +bool Subprocess::Done() const { + return fd_ == -1; +} + +const string& Subprocess::GetOutput() const { + return buf_; +} + +int SubprocessSet::interrupted_; + +void SubprocessSet::SetInterruptedFlag(int signum) { + interrupted_ = signum; +} + +void SubprocessSet::HandlePendingInterruption() { + sigset_t pending; + sigemptyset(&pending); + if (sigpending(&pending) == -1) { + perror("ninja: sigpending"); + return; + } + if (sigismember(&pending, SIGINT)) + interrupted_ = SIGINT; + else if (sigismember(&pending, SIGTERM)) + interrupted_ = SIGTERM; + else if (sigismember(&pending, SIGHUP)) + interrupted_ = SIGHUP; +} + +SubprocessSet::SubprocessSet() { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + if (sigprocmask(SIG_BLOCK, &set, &old_mask_) < 0) + Fatal("sigprocmask: %s", strerror(errno)); + + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SetInterruptedFlag; + if (sigaction(SIGINT, &act, &old_int_act_) < 0) + Fatal("sigaction: %s", strerror(errno)); + if (sigaction(SIGTERM, &act, &old_term_act_) < 0) + Fatal("sigaction: %s", strerror(errno)); + if (sigaction(SIGHUP, &act, &old_hup_act_) < 0) + Fatal("sigaction: %s", strerror(errno)); +} + +SubprocessSet::~SubprocessSet() { + Clear(); + + if (sigaction(SIGINT, &old_int_act_, 0) < 0) + Fatal("sigaction: %s", strerror(errno)); + if (sigaction(SIGTERM, &old_term_act_, 0) < 0) + Fatal("sigaction: %s", strerror(errno)); + if (sigaction(SIGHUP, &old_hup_act_, 0) < 0) + Fatal("sigaction: %s", strerror(errno)); + if (sigprocmask(SIG_SETMASK, &old_mask_, 0) < 0) + Fatal("sigprocmask: %s", strerror(errno)); +} + +Subprocess *SubprocessSet::Add(const string& command, bool use_console) { + Subprocess *subprocess = new Subprocess(use_console); + if (!subprocess->Start(this, command)) { + delete subprocess; + return 0; + } + running_.push_back(subprocess); + return subprocess; +} + +#ifdef USE_PPOLL +bool SubprocessSet::DoWork() { + vector fds; + nfds_t nfds = 0; + + for (vector::iterator i = running_.begin(); + i != running_.end(); ++i) { + int fd = (*i)->fd_; + if (fd < 0) + continue; + pollfd pfd = { fd, POLLIN | POLLPRI, 0 }; + fds.push_back(pfd); + ++nfds; + } + + interrupted_ = 0; + int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_); + if (ret == -1) { + if (errno != EINTR) { + perror("ninja: ppoll"); + return false; + } + return IsInterrupted(); + } + + HandlePendingInterruption(); + if (IsInterrupted()) + return true; + + nfds_t cur_nfd = 0; + for (vector::iterator i = running_.begin(); + i != running_.end(); ) { + int fd = (*i)->fd_; + if (fd < 0) + continue; + assert(fd == fds[cur_nfd].fd); + if (fds[cur_nfd++].revents) { + (*i)->OnPipeReady(); + if ((*i)->Done()) { + finished_.push(*i); + i = running_.erase(i); + continue; + } + } + ++i; + } + + return IsInterrupted(); +} + +#else // !defined(USE_PPOLL) +bool SubprocessSet::DoWork() { + fd_set set; + int nfds = 0; + FD_ZERO(&set); + + for (vector::iterator i = running_.begin(); + i != running_.end(); ++i) { + int fd = (*i)->fd_; + if (fd >= 0) { + FD_SET(fd, &set); + if (nfds < fd+1) + nfds = fd+1; + } + } + + interrupted_ = 0; + int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_); + if (ret == -1) { + if (errno != EINTR) { + perror("ninja: pselect"); + return false; + } + return IsInterrupted(); + } + + HandlePendingInterruption(); + if (IsInterrupted()) + return true; + + for (vector::iterator i = running_.begin(); + i != running_.end(); ) { + int fd = (*i)->fd_; + if (fd >= 0 && FD_ISSET(fd, &set)) { + (*i)->OnPipeReady(); + if ((*i)->Done()) { + finished_.push(*i); + i = running_.erase(i); + continue; + } + } + ++i; + } + + return IsInterrupted(); +} +#endif // !defined(USE_PPOLL) + +Subprocess* SubprocessSet::NextFinished() { + if (finished_.empty()) + return NULL; + Subprocess* subproc = finished_.front(); + finished_.pop(); + return subproc; +} + +void SubprocessSet::Clear() { + for (vector::iterator i = running_.begin(); + i != running_.end(); ++i) + // Since the foreground process is in our process group, it will receive + // the interruption signal (i.e. SIGINT or SIGTERM) at the same time as us. + if (!(*i)->use_console_) + kill(-(*i)->pid_, interrupted_); + for (vector::iterator i = running_.begin(); + i != running_.end(); ++i) + delete *i; + running_.clear(); +} diff --git a/src/3rdparty/ninja/src/subprocess-win32.cc b/src/3rdparty/ninja/src/subprocess-win32.cc new file mode 100644 index 00000000000..4bab71939d6 --- /dev/null +++ b/src/3rdparty/ninja/src/subprocess-win32.cc @@ -0,0 +1,292 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "subprocess.h" + +#include +#include + +#include + +#include "util.h" + +Subprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(), + is_reading_(false), + use_console_(use_console) { +} + +Subprocess::~Subprocess() { + if (pipe_) { + if (!CloseHandle(pipe_)) + Win32Fatal("CloseHandle"); + } + // Reap child if forgotten. + if (child_) + Finish(); +} + +HANDLE Subprocess::SetupPipe(HANDLE ioport) { + char pipe_name[100]; + snprintf(pipe_name, sizeof(pipe_name), + "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this); + + pipe_ = ::CreateNamedPipeA(pipe_name, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, + 0, 0, INFINITE, NULL); + if (pipe_ == INVALID_HANDLE_VALUE) + Win32Fatal("CreateNamedPipe"); + + if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0)) + Win32Fatal("CreateIoCompletionPort"); + + memset(&overlapped_, 0, sizeof(overlapped_)); + if (!ConnectNamedPipe(pipe_, &overlapped_) && + GetLastError() != ERROR_IO_PENDING) { + Win32Fatal("ConnectNamedPipe"); + } + + // Get the write end of the pipe as a handle inheritable across processes. + HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, 0, NULL); + HANDLE output_write_child; + if (!DuplicateHandle(GetCurrentProcess(), output_write_handle, + GetCurrentProcess(), &output_write_child, + 0, TRUE, DUPLICATE_SAME_ACCESS)) { + Win32Fatal("DuplicateHandle"); + } + CloseHandle(output_write_handle); + + return output_write_child; +} + +bool Subprocess::Start(SubprocessSet* set, const string& command) { + HANDLE child_pipe = SetupPipe(set->ioport_); + + SECURITY_ATTRIBUTES security_attributes; + memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES)); + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = TRUE; + // Must be inheritable so subprocesses can dup to children. + HANDLE nul = CreateFile("NUL", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, 0, NULL); + if (nul == INVALID_HANDLE_VALUE) + Fatal("couldn't open nul"); + + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(startup_info)); + startup_info.cb = sizeof(STARTUPINFO); + if (!use_console_) { + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = nul; + startup_info.hStdOutput = child_pipe; + startup_info.hStdError = child_pipe; + } + // In the console case, child_pipe is still inherited by the child and closed + // when the subprocess finishes, which then notifies ninja. + + PROCESS_INFORMATION process_info; + memset(&process_info, 0, sizeof(process_info)); + + // Ninja handles ctrl-c, except for subprocesses in console pools. + DWORD process_flags = use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP; + + // Do not prepend 'cmd /c' on Windows, this breaks command + // lines greater than 8,191 chars. + if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL, + /* inherit handles */ TRUE, process_flags, + NULL, NULL, + &startup_info, &process_info)) { + DWORD error = GetLastError(); + if (error == ERROR_FILE_NOT_FOUND) { + // File (program) not found error is treated as a normal build + // action failure. + if (child_pipe) + CloseHandle(child_pipe); + CloseHandle(pipe_); + CloseHandle(nul); + pipe_ = NULL; + // child_ is already NULL; + buf_ = "CreateProcess failed: The system cannot find the file " + "specified.\n"; + return true; + } else { + Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal + } + } + + // Close pipe channel only used by the child. + if (child_pipe) + CloseHandle(child_pipe); + CloseHandle(nul); + + CloseHandle(process_info.hThread); + child_ = process_info.hProcess; + + return true; +} + +void Subprocess::OnPipeReady() { + DWORD bytes; + if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + CloseHandle(pipe_); + pipe_ = NULL; + return; + } + Win32Fatal("GetOverlappedResult"); + } + + if (is_reading_ && bytes) + buf_.append(overlapped_buf_, bytes); + + memset(&overlapped_, 0, sizeof(overlapped_)); + is_reading_ = true; + if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_), + &bytes, &overlapped_)) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + CloseHandle(pipe_); + pipe_ = NULL; + return; + } + if (GetLastError() != ERROR_IO_PENDING) + Win32Fatal("ReadFile"); + } + + // Even if we read any bytes in the readfile call, we'll enter this + // function again later and get them at that point. +} + +ExitStatus Subprocess::Finish() { + if (!child_) + return ExitFailure; + + // TODO: add error handling for all of these. + WaitForSingleObject(child_, INFINITE); + + DWORD exit_code = 0; + GetExitCodeProcess(child_, &exit_code); + + CloseHandle(child_); + child_ = NULL; + + return exit_code == 0 ? ExitSuccess : + exit_code == CONTROL_C_EXIT ? ExitInterrupted : + ExitFailure; +} + +bool Subprocess::Done() const { + return pipe_ == NULL; +} + +const string& Subprocess::GetOutput() const { + return buf_; +} + +HANDLE SubprocessSet::ioport_; + +SubprocessSet::SubprocessSet() { + ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); + if (!ioport_) + Win32Fatal("CreateIoCompletionPort"); + if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE)) + Win32Fatal("SetConsoleCtrlHandler"); +} + +SubprocessSet::~SubprocessSet() { + Clear(); + + SetConsoleCtrlHandler(NotifyInterrupted, FALSE); + CloseHandle(ioport_); +} + +BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) { + if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { + if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL)) + Win32Fatal("PostQueuedCompletionStatus"); + return TRUE; + } + + return FALSE; +} + +Subprocess *SubprocessSet::Add(const string& command, bool use_console) { + Subprocess *subprocess = new Subprocess(use_console); + if (!subprocess->Start(this, command)) { + delete subprocess; + return 0; + } + if (subprocess->child_) + running_.push_back(subprocess); + else + finished_.push(subprocess); + return subprocess; +} + +bool SubprocessSet::DoWork() { + DWORD bytes_read; + Subprocess* subproc; + OVERLAPPED* overlapped; + + if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc, + &overlapped, INFINITE)) { + if (GetLastError() != ERROR_BROKEN_PIPE) + Win32Fatal("GetQueuedCompletionStatus"); + } + + if (!subproc) // A NULL subproc indicates that we were interrupted and is + // delivered by NotifyInterrupted above. + return true; + + subproc->OnPipeReady(); + + if (subproc->Done()) { + vector::iterator end = + remove(running_.begin(), running_.end(), subproc); + if (running_.end() != end) { + finished_.push(subproc); + running_.resize(end - running_.begin()); + } + } + + return false; +} + +Subprocess* SubprocessSet::NextFinished() { + if (finished_.empty()) + return NULL; + Subprocess* subproc = finished_.front(); + finished_.pop(); + return subproc; +} + +void SubprocessSet::Clear() { + for (vector::iterator i = running_.begin(); + i != running_.end(); ++i) { + // Since the foreground process is in our process group, it will receive a + // CTRL_C_EVENT or CTRL_BREAK_EVENT at the same time as us. + if ((*i)->child_ && !(*i)->use_console_) { + if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, + GetProcessId((*i)->child_))) { + Win32Fatal("GenerateConsoleCtrlEvent"); + } + } + } + for (vector::iterator i = running_.begin(); + i != running_.end(); ++i) + delete *i; + running_.clear(); +} diff --git a/src/3rdparty/ninja/src/subprocess.h b/src/3rdparty/ninja/src/subprocess.h new file mode 100644 index 00000000000..b2d486ca400 --- /dev/null +++ b/src/3rdparty/ninja/src/subprocess.h @@ -0,0 +1,114 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_SUBPROCESS_H_ +#define NINJA_SUBPROCESS_H_ + +#include +#include +#include +using namespace std; + +#ifdef _WIN32 +#include +#else +#include +#endif + +// ppoll() exists on FreeBSD, but only on newer versions. +#ifdef __FreeBSD__ +# include +# if defined USE_PPOLL && __FreeBSD_version < 1002000 +# undef USE_PPOLL +# endif +#endif + +#include "exit_status.h" + +/// Subprocess wraps a single async subprocess. It is entirely +/// passive: it expects the caller to notify it when its fds are ready +/// for reading, as well as call Finish() to reap the child once done() +/// is true. +struct Subprocess { + ~Subprocess(); + + /// Returns ExitSuccess on successful process exit, ExitInterrupted if + /// the process was interrupted, ExitFailure if it otherwise failed. + ExitStatus Finish(); + + bool Done() const; + + const string& GetOutput() const; + + private: + Subprocess(bool use_console); + bool Start(struct SubprocessSet* set, const string& command); + void OnPipeReady(); + + string buf_; + +#ifdef _WIN32 + /// Set up pipe_ as the parent-side pipe of the subprocess; return the + /// other end of the pipe, usable in the child process. + HANDLE SetupPipe(HANDLE ioport); + + HANDLE child_; + HANDLE pipe_; + OVERLAPPED overlapped_; + char overlapped_buf_[4 << 10]; + bool is_reading_; +#else + int fd_; + pid_t pid_; +#endif + bool use_console_; + + friend struct SubprocessSet; +}; + +/// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses. +/// DoWork() waits for any state change in subprocesses; finished_ +/// is a queue of subprocesses as they finish. +struct SubprocessSet { + SubprocessSet(); + ~SubprocessSet(); + + Subprocess* Add(const string& command, bool use_console = false); + bool DoWork(); + Subprocess* NextFinished(); + void Clear(); + + vector running_; + queue finished_; + +#ifdef _WIN32 + static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType); + static HANDLE ioport_; +#else + static void SetInterruptedFlag(int signum); + static void HandlePendingInterruption(); + /// Store the signal number that causes the interruption. + /// 0 if not interruption. + static int interrupted_; + + static bool IsInterrupted() { return interrupted_ != 0; } + + struct sigaction old_int_act_; + struct sigaction old_term_act_; + struct sigaction old_hup_act_; + sigset_t old_mask_; +#endif +}; + +#endif // NINJA_SUBPROCESS_H_ diff --git a/src/3rdparty/ninja/src/subprocess_test.cc b/src/3rdparty/ninja/src/subprocess_test.cc new file mode 100644 index 00000000000..0a8c2061b7f --- /dev/null +++ b/src/3rdparty/ninja/src/subprocess_test.cc @@ -0,0 +1,261 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "subprocess.h" + +#include "test.h" + +#ifndef _WIN32 +// SetWithLots need setrlimit. +#include +#include +#include +#include +#endif + +namespace { + +#ifdef _WIN32 +const char* kSimpleCommand = "cmd /c dir \\"; +#else +const char* kSimpleCommand = "ls /"; +#endif + +struct SubprocessTest : public testing::Test { + SubprocessSet subprocs_; +}; + +} // anonymous namespace + +// Run a command that fails and emits to stderr. +TEST_F(SubprocessTest, BadCommandStderr) { + Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + // Pretend we discovered that stderr was ready for writing. + subprocs_.DoWork(); + } + + EXPECT_EQ(ExitFailure, subproc->Finish()); + EXPECT_NE("", subproc->GetOutput()); +} + +// Run a command that does not exist +TEST_F(SubprocessTest, NoSuchCommand) { + Subprocess* subproc = subprocs_.Add("ninja_no_such_command"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + // Pretend we discovered that stderr was ready for writing. + subprocs_.DoWork(); + } + + EXPECT_EQ(ExitFailure, subproc->Finish()); + EXPECT_NE("", subproc->GetOutput()); +#ifdef _WIN32 + ASSERT_EQ("CreateProcess failed: The system cannot find the file " + "specified.\n", subproc->GetOutput()); +#endif +} + +#ifndef _WIN32 + +TEST_F(SubprocessTest, InterruptChild) { + Subprocess* subproc = subprocs_.Add("kill -INT $$"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + subprocs_.DoWork(); + } + + EXPECT_EQ(ExitInterrupted, subproc->Finish()); +} + +TEST_F(SubprocessTest, InterruptParent) { + Subprocess* subproc = subprocs_.Add("kill -INT $PPID ; sleep 1"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + bool interrupted = subprocs_.DoWork(); + if (interrupted) + return; + } + + ASSERT_FALSE("We should have been interrupted"); +} + +TEST_F(SubprocessTest, InterruptChildWithSigTerm) { + Subprocess* subproc = subprocs_.Add("kill -TERM $$"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + subprocs_.DoWork(); + } + + EXPECT_EQ(ExitInterrupted, subproc->Finish()); +} + +TEST_F(SubprocessTest, InterruptParentWithSigTerm) { + Subprocess* subproc = subprocs_.Add("kill -TERM $PPID ; sleep 1"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + bool interrupted = subprocs_.DoWork(); + if (interrupted) + return; + } + + ASSERT_FALSE("We should have been interrupted"); +} + +TEST_F(SubprocessTest, InterruptChildWithSigHup) { + Subprocess* subproc = subprocs_.Add("kill -HUP $$"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + subprocs_.DoWork(); + } + + EXPECT_EQ(ExitInterrupted, subproc->Finish()); +} + +TEST_F(SubprocessTest, InterruptParentWithSigHup) { + Subprocess* subproc = subprocs_.Add("kill -HUP $PPID ; sleep 1"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + bool interrupted = subprocs_.DoWork(); + if (interrupted) + return; + } + + ASSERT_FALSE("We should have been interrupted"); +} + +TEST_F(SubprocessTest, Console) { + // Skip test if we don't have the console ourselves. + if (isatty(0) && isatty(1) && isatty(2)) { + Subprocess* subproc = + subprocs_.Add("test -t 0 -a -t 1 -a -t 2", /*use_console=*/true); + ASSERT_NE((Subprocess*)0, subproc); + + while (!subproc->Done()) { + subprocs_.DoWork(); + } + + EXPECT_EQ(ExitSuccess, subproc->Finish()); + } +} + +#endif + +TEST_F(SubprocessTest, SetWithSingle) { + Subprocess* subproc = subprocs_.Add(kSimpleCommand); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + subprocs_.DoWork(); + } + ASSERT_EQ(ExitSuccess, subproc->Finish()); + ASSERT_NE("", subproc->GetOutput()); + + ASSERT_EQ(1u, subprocs_.finished_.size()); +} + +TEST_F(SubprocessTest, SetWithMulti) { + Subprocess* processes[3]; + const char* kCommands[3] = { + kSimpleCommand, +#ifdef _WIN32 + "cmd /c echo hi", + "cmd /c time /t", +#else + "whoami", + "pwd", +#endif + }; + + for (int i = 0; i < 3; ++i) { + processes[i] = subprocs_.Add(kCommands[i]); + ASSERT_NE((Subprocess *) 0, processes[i]); + } + + ASSERT_EQ(3u, subprocs_.running_.size()); + for (int i = 0; i < 3; ++i) { + ASSERT_FALSE(processes[i]->Done()); + ASSERT_EQ("", processes[i]->GetOutput()); + } + + while (!processes[0]->Done() || !processes[1]->Done() || + !processes[2]->Done()) { + ASSERT_GT(subprocs_.running_.size(), 0u); + subprocs_.DoWork(); + } + + ASSERT_EQ(0u, subprocs_.running_.size()); + ASSERT_EQ(3u, subprocs_.finished_.size()); + + for (int i = 0; i < 3; ++i) { + ASSERT_EQ(ExitSuccess, processes[i]->Finish()); + ASSERT_NE("", processes[i]->GetOutput()); + delete processes[i]; + } +} + +#if defined(USE_PPOLL) +TEST_F(SubprocessTest, SetWithLots) { + // Arbitrary big number; needs to be over 1024 to confirm we're no longer + // hostage to pselect. + const unsigned kNumProcs = 1025; + + // Make sure [ulimit -n] isn't going to stop us from working. + rlimit rlim; + ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim)); + if (rlim.rlim_cur < kNumProcs) { + printf("Raise [ulimit -n] above %u (currently %lu) to make this test go\n", + kNumProcs, rlim.rlim_cur); + return; + } + + vector procs; + for (size_t i = 0; i < kNumProcs; ++i) { + Subprocess* subproc = subprocs_.Add("/bin/echo"); + ASSERT_NE((Subprocess *) 0, subproc); + procs.push_back(subproc); + } + while (!subprocs_.running_.empty()) + subprocs_.DoWork(); + for (size_t i = 0; i < procs.size(); ++i) { + ASSERT_EQ(ExitSuccess, procs[i]->Finish()); + ASSERT_NE("", procs[i]->GetOutput()); + } + ASSERT_EQ(kNumProcs, subprocs_.finished_.size()); +} +#endif // !__APPLE__ && !_WIN32 + +// TODO: this test could work on Windows, just not sure how to simply +// read stdin. +#ifndef _WIN32 +// Verify that a command that attempts to read stdin correctly thinks +// that stdin is closed. +TEST_F(SubprocessTest, ReadStdin) { + Subprocess* subproc = subprocs_.Add("cat -"); + while (!subproc->Done()) { + subprocs_.DoWork(); + } + ASSERT_EQ(ExitSuccess, subproc->Finish()); + ASSERT_EQ(1u, subprocs_.finished_.size()); +} +#endif // _WIN32 diff --git a/src/3rdparty/ninja/src/test.cc b/src/3rdparty/ninja/src/test.cc new file mode 100644 index 00000000000..a9816bc232d --- /dev/null +++ b/src/3rdparty/ninja/src/test.cc @@ -0,0 +1,235 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef _WIN32 +#include // Has to be before util.h is included. +#endif + +#include "test.h" + +#include + +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "build_log.h" +#include "graph.h" +#include "manifest_parser.h" +#include "util.h" + +namespace { + +#ifdef _WIN32 +#ifndef _mktemp_s +/// mingw has no mktemp. Implement one with the same type as the one +/// found in the Windows API. +int _mktemp_s(char* templ) { + char* ofs = strchr(templ, 'X'); + sprintf(ofs, "%d", rand() % 1000000); + return 0; +} +#endif + +/// Windows has no mkdtemp. Implement it in terms of _mktemp_s. +char* mkdtemp(char* name_template) { + int err = _mktemp_s(name_template); + if (err < 0) { + perror("_mktemp_s"); + return NULL; + } + + err = _mkdir(name_template); + if (err < 0) { + perror("mkdir"); + return NULL; + } + + return name_template; +} +#endif // _WIN32 + +string GetSystemTempDir() { +#ifdef _WIN32 + char buf[1024]; + if (!GetTempPath(sizeof(buf), buf)) + return ""; + return buf; +#else + const char* tempdir = getenv("TMPDIR"); + if (tempdir) + return tempdir; + return "/tmp"; +#endif +} + +} // anonymous namespace + +StateTestWithBuiltinRules::StateTestWithBuiltinRules() { + AddCatRule(&state_); +} + +void StateTestWithBuiltinRules::AddCatRule(State* state) { + AssertParse(state, +"rule cat\n" +" command = cat $in > $out\n"); +} + +Node* StateTestWithBuiltinRules::GetNode(const string& path) { + EXPECT_FALSE(strpbrk(path.c_str(), "/\\")); + return state_.GetNode(path, 0); +} + +void AssertParse(State* state, const char* input, + ManifestParserOptions opts) { + ManifestParser parser(state, NULL, opts); + string err; + EXPECT_TRUE(parser.ParseTest(input, &err)); + ASSERT_EQ("", err); + VerifyGraph(*state); +} + +void AssertHash(const char* expected, uint64_t actual) { + ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual); +} + +void VerifyGraph(const State& state) { + for (vector::const_iterator e = state.edges_.begin(); + e != state.edges_.end(); ++e) { + // All edges need at least one output. + EXPECT_FALSE((*e)->outputs_.empty()); + // Check that the edge's inputs have the edge as out-edge. + for (vector::const_iterator in_node = (*e)->inputs_.begin(); + in_node != (*e)->inputs_.end(); ++in_node) { + const vector& out_edges = (*in_node)->out_edges(); + EXPECT_NE(find(out_edges.begin(), out_edges.end(), *e), + out_edges.end()); + } + // Check that the edge's outputs have the edge as in-edge. + for (vector::const_iterator out_node = (*e)->outputs_.begin(); + out_node != (*e)->outputs_.end(); ++out_node) { + EXPECT_EQ((*out_node)->in_edge(), *e); + } + } + + // The union of all in- and out-edges of each nodes should be exactly edges_. + set node_edge_set; + for (State::Paths::const_iterator p = state.paths_.begin(); + p != state.paths_.end(); ++p) { + const Node* n = p->second; + if (n->in_edge()) + node_edge_set.insert(n->in_edge()); + node_edge_set.insert(n->out_edges().begin(), n->out_edges().end()); + } + set edge_set(state.edges_.begin(), state.edges_.end()); + EXPECT_EQ(node_edge_set, edge_set); +} + +void VirtualFileSystem::Create(const string& path, + const string& contents) { + files_[path].mtime = now_; + files_[path].contents = contents; + files_created_.insert(path); +} + +TimeStamp VirtualFileSystem::Stat(const string& path, string* err) const { + FileMap::const_iterator i = files_.find(path); + if (i != files_.end()) { + *err = i->second.stat_error; + return i->second.mtime; + } + return 0; +} + +bool VirtualFileSystem::WriteFile(const string& path, const string& contents) { + Create(path, contents); + return true; +} + +bool VirtualFileSystem::MakeDir(const string& path) { + directories_made_.push_back(path); + return true; // success +} + +FileReader::Status VirtualFileSystem::ReadFile(const string& path, + string* contents, + string* err) { + files_read_.push_back(path); + FileMap::iterator i = files_.find(path); + if (i != files_.end()) { + *contents = i->second.contents; + return Okay; + } + *err = strerror(ENOENT); + return NotFound; +} + +int VirtualFileSystem::RemoveFile(const string& path) { + if (find(directories_made_.begin(), directories_made_.end(), path) + != directories_made_.end()) + return -1; + FileMap::iterator i = files_.find(path); + if (i != files_.end()) { + files_.erase(i); + files_removed_.insert(path); + return 0; + } else { + return 1; + } +} + +void ScopedTempDir::CreateAndEnter(const string& name) { + // First change into the system temp dir and save it for cleanup. + start_dir_ = GetSystemTempDir(); + if (start_dir_.empty()) + Fatal("couldn't get system temp dir"); + if (chdir(start_dir_.c_str()) < 0) + Fatal("chdir: %s", strerror(errno)); + + // Create a temporary subdirectory of that. + char name_template[1024]; + strcpy(name_template, name.c_str()); + strcat(name_template, "-XXXXXX"); + char* tempname = mkdtemp(name_template); + if (!tempname) + Fatal("mkdtemp: %s", strerror(errno)); + temp_dir_name_ = tempname; + + // chdir into the new temporary directory. + if (chdir(temp_dir_name_.c_str()) < 0) + Fatal("chdir: %s", strerror(errno)); +} + +void ScopedTempDir::Cleanup() { + if (temp_dir_name_.empty()) + return; // Something went wrong earlier. + + // Move out of the directory we're about to clobber. + if (chdir(start_dir_.c_str()) < 0) + Fatal("chdir: %s", strerror(errno)); + +#ifdef _WIN32 + string command = "rmdir /s /q " + temp_dir_name_; +#else + string command = "rm -rf " + temp_dir_name_; +#endif + if (system(command.c_str()) < 0) + Fatal("system: %s", strerror(errno)); + + temp_dir_name_.clear(); +} diff --git a/src/3rdparty/ninja/src/test.h b/src/3rdparty/ninja/src/test.h new file mode 100644 index 00000000000..3bce8f75aef --- /dev/null +++ b/src/3rdparty/ninja/src/test.h @@ -0,0 +1,184 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_TEST_H_ +#define NINJA_TEST_H_ + +#include "disk_interface.h" +#include "manifest_parser.h" +#include "state.h" +#include "util.h" + +// A tiny testing framework inspired by googletest, but much simpler and +// faster to compile. It supports most things commonly used from googltest. The +// most noticeable things missing: EXPECT_* and ASSERT_* don't support +// streaming notes to them with operator<<, and for failing tests the lhs and +// rhs are not printed. That's so that this header does not have to include +// sstream, which slows down building ninja_test almost 20%. +namespace testing { +class Test { + bool failed_; + int assertion_failures_; + public: + Test() : failed_(false), assertion_failures_(0) {} + virtual ~Test() {} + virtual void SetUp() {} + virtual void TearDown() {} + virtual void Run() = 0; + + bool Failed() const { return failed_; } + int AssertionFailures() const { return assertion_failures_; } + void AddAssertionFailure() { assertion_failures_++; } + bool Check(bool condition, const char* file, int line, const char* error); +}; +} + +void RegisterTest(testing::Test* (*)(), const char*); + +extern testing::Test* g_current_test; +#define TEST_F_(x, y, name) \ + struct y : public x { \ + static testing::Test* Create() { return g_current_test = new y; } \ + virtual void Run(); \ + }; \ + struct Register##y { \ + Register##y() { RegisterTest(y::Create, name); } \ + }; \ + Register##y g_register_##y; \ + void y::Run() + +#define TEST_F(x, y) TEST_F_(x, x##y, #x "." #y) +#define TEST(x, y) TEST_F_(testing::Test, x##y, #x "." #y) + +#define EXPECT_EQ(a, b) \ + g_current_test->Check(a == b, __FILE__, __LINE__, #a " == " #b) +#define EXPECT_NE(a, b) \ + g_current_test->Check(a != b, __FILE__, __LINE__, #a " != " #b) +#define EXPECT_GT(a, b) \ + g_current_test->Check(a > b, __FILE__, __LINE__, #a " > " #b) +#define EXPECT_LT(a, b) \ + g_current_test->Check(a < b, __FILE__, __LINE__, #a " < " #b) +#define EXPECT_GE(a, b) \ + g_current_test->Check(a >= b, __FILE__, __LINE__, #a " >= " #b) +#define EXPECT_LE(a, b) \ + g_current_test->Check(a <= b, __FILE__, __LINE__, #a " <= " #b) +#define EXPECT_TRUE(a) \ + g_current_test->Check(static_cast(a), __FILE__, __LINE__, #a) +#define EXPECT_FALSE(a) \ + g_current_test->Check(!static_cast(a), __FILE__, __LINE__, #a) + +#define ASSERT_EQ(a, b) \ + if (!EXPECT_EQ(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_NE(a, b) \ + if (!EXPECT_NE(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_GT(a, b) \ + if (!EXPECT_GT(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_LT(a, b) \ + if (!EXPECT_LT(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_GE(a, b) \ + if (!EXPECT_GE(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_LE(a, b) \ + if (!EXPECT_LE(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_TRUE(a) \ + if (!EXPECT_TRUE(a)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_FALSE(a) \ + if (!EXPECT_FALSE(a)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_NO_FATAL_FAILURE(a) \ + { \ + int fail_count = g_current_test->AssertionFailures(); \ + a; \ + if (fail_count != g_current_test->AssertionFailures()) { \ + g_current_test->AddAssertionFailure(); \ + return; \ + } \ + } + +// Support utilites for tests. + +struct Node; + +/// A base test fixture that includes a State object with a +/// builtin "cat" rule. +struct StateTestWithBuiltinRules : public testing::Test { + StateTestWithBuiltinRules(); + + /// Add a "cat" rule to \a state. Used by some tests; it's + /// otherwise done by the ctor to state_. + void AddCatRule(State* state); + + /// Short way to get a Node by its path from state_. + Node* GetNode(const string& path); + + State state_; +}; + +void AssertParse(State* state, const char* input, + ManifestParserOptions = ManifestParserOptions()); +void AssertHash(const char* expected, uint64_t actual); +void VerifyGraph(const State& state); + +/// An implementation of DiskInterface that uses an in-memory representation +/// of disk state. It also logs file accesses and directory creations +/// so it can be used by tests to verify disk access patterns. +struct VirtualFileSystem : public DiskInterface { + VirtualFileSystem() : now_(1) {} + + /// "Create" a file with contents. + void Create(const string& path, const string& contents); + + /// Tick "time" forwards; subsequent file operations will be newer than + /// previous ones. + int Tick() { + return ++now_; + } + + // DiskInterface + virtual TimeStamp Stat(const string& path, string* err) const; + virtual bool WriteFile(const string& path, const string& contents); + virtual bool MakeDir(const string& path); + virtual Status ReadFile(const string& path, string* contents, string* err); + virtual int RemoveFile(const string& path); + + /// An entry for a single in-memory file. + struct Entry { + int mtime; + string stat_error; // If mtime is -1. + string contents; + }; + + vector directories_made_; + vector files_read_; + typedef map FileMap; + FileMap files_; + set files_removed_; + set files_created_; + + /// A simple fake timestamp for file operations. + int now_; +}; + +struct ScopedTempDir { + /// Create a temporary directory and chdir into it. + void CreateAndEnter(const string& name); + + /// Clean up the temporary directory. + void Cleanup(); + + /// The temp directory containing our dir. + string start_dir_; + /// The subdirectory name for our dir, or empty if it hasn't been set up. + string temp_dir_name_; +}; + +#endif // NINJA_TEST_H_ diff --git a/src/3rdparty/ninja/src/timestamp.h b/src/3rdparty/ninja/src/timestamp.h new file mode 100644 index 00000000000..cee7ba8f21b --- /dev/null +++ b/src/3rdparty/ninja/src/timestamp.h @@ -0,0 +1,24 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_TIMESTAMP_H_ +#define NINJA_TIMESTAMP_H_ + +// When considering file modification times we only care to compare +// them against one another -- we never convert them to an absolute +// real time. On POSIX we use time_t (seconds since epoch) and on +// Windows we use a different value. Both fit in an int. +typedef int TimeStamp; + +#endif // NINJA_TIMESTAMP_H_ diff --git a/src/3rdparty/ninja/src/util.cc b/src/3rdparty/ninja/src/util.cc new file mode 100644 index 00000000000..ae94d346bc5 --- /dev/null +++ b/src/3rdparty/ninja/src/util.cc @@ -0,0 +1,606 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util.h" + +#ifdef __CYGWIN__ +#include +#include +#elif defined( _WIN32) +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include + +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(__SVR4) && defined(__sun) +#include +#include +#elif defined(_AIX) +#include +#elif defined(linux) || defined(__GLIBC__) +#include +#endif + +#include "edit_distance.h" +#include "metrics.h" + +void Fatal(const char* msg, ...) { + va_list ap; + fprintf(stderr, "ninja: fatal: "); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n"); +#ifdef _WIN32 + // On Windows, some tools may inject extra threads. + // exit() may block on locks held by those threads, so forcibly exit. + fflush(stderr); + fflush(stdout); + ExitProcess(1); +#else + exit(1); +#endif +} + +void Warning(const char* msg, ...) { + va_list ap; + fprintf(stderr, "ninja: warning: "); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +void Error(const char* msg, ...) { + va_list ap; + fprintf(stderr, "ninja: error: "); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +bool CanonicalizePath(string* path, uint64_t* slash_bits, string* err) { + METRIC_RECORD("canonicalize str"); + size_t len = path->size(); + char* str = 0; + if (len > 0) + str = &(*path)[0]; + if (!CanonicalizePath(str, &len, slash_bits, err)) + return false; + path->resize(len); + return true; +} + +static bool IsPathSeparator(char c) { +#ifdef _WIN32 + return c == '/' || c == '\\'; +#else + return c == '/'; +#endif +} + +bool CanonicalizePath(char* path, size_t* len, uint64_t* slash_bits, + string* err) { + // WARNING: this function is performance-critical; please benchmark + // any changes you make to it. + METRIC_RECORD("canonicalize path"); + if (*len == 0) { + *err = "empty path"; + return false; + } + + const int kMaxPathComponents = 60; + char* components[kMaxPathComponents]; + int component_count = 0; + + char* start = path; + char* dst = start; + const char* src = start; + const char* end = start + *len; + + if (IsPathSeparator(*src)) { +#ifdef _WIN32 + + // network path starts with // + if (*len > 1 && IsPathSeparator(*(src + 1))) { + src += 2; + dst += 2; + } else { + ++src; + ++dst; + } +#else + ++src; + ++dst; +#endif + } + + while (src < end) { + if (*src == '.') { + if (src + 1 == end || IsPathSeparator(src[1])) { + // '.' component; eliminate. + src += 2; + continue; + } else if (src[1] == '.' && (src + 2 == end || IsPathSeparator(src[2]))) { + // '..' component. Back up if possible. + if (component_count > 0) { + dst = components[component_count - 1]; + src += 3; + --component_count; + } else { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + } + continue; + } + } + + if (IsPathSeparator(*src)) { + src++; + continue; + } + + if (component_count == kMaxPathComponents) + Fatal("path has too many components : %s", path); + components[component_count] = dst; + ++component_count; + + while (src != end && !IsPathSeparator(*src)) + *dst++ = *src++; + *dst++ = *src++; // Copy '/' or final \0 character as well. + } + + if (dst == start) { + *dst++ = '.'; + *dst++ = '\0'; + } + + *len = dst - start - 1; +#ifdef _WIN32 + uint64_t bits = 0; + uint64_t bits_mask = 1; + + for (char* c = start; c < start + *len; ++c) { + switch (*c) { + case '\\': + bits |= bits_mask; + *c = '/'; + // Intentional fallthrough. + case '/': + bits_mask <<= 1; + } + } + + *slash_bits = bits; +#else + *slash_bits = 0; +#endif + return true; +} + +static inline bool IsKnownShellSafeCharacter(char ch) { + if ('A' <= ch && ch <= 'Z') return true; + if ('a' <= ch && ch <= 'z') return true; + if ('0' <= ch && ch <= '9') return true; + + switch (ch) { + case '_': + case '+': + case '-': + case '.': + case '/': + return true; + default: + return false; + } +} + +static inline bool IsKnownWin32SafeCharacter(char ch) { + switch (ch) { + case ' ': + case '"': + return false; + default: + return true; + } +} + +static inline bool StringNeedsShellEscaping(const string& input) { + for (size_t i = 0; i < input.size(); ++i) { + if (!IsKnownShellSafeCharacter(input[i])) return true; + } + return false; +} + +static inline bool StringNeedsWin32Escaping(const string& input) { + for (size_t i = 0; i < input.size(); ++i) { + if (!IsKnownWin32SafeCharacter(input[i])) return true; + } + return false; +} + +void GetShellEscapedString(const string& input, string* result) { + assert(result); + + if (!StringNeedsShellEscaping(input)) { + result->append(input); + return; + } + + const char kQuote = '\''; + const char kEscapeSequence[] = "'\\'"; + + result->push_back(kQuote); + + string::const_iterator span_begin = input.begin(); + for (string::const_iterator it = input.begin(), end = input.end(); it != end; + ++it) { + if (*it == kQuote) { + result->append(span_begin, it); + result->append(kEscapeSequence); + span_begin = it; + } + } + result->append(span_begin, input.end()); + result->push_back(kQuote); +} + + +void GetWin32EscapedString(const string& input, string* result) { + assert(result); + if (!StringNeedsWin32Escaping(input)) { + result->append(input); + return; + } + + const char kQuote = '"'; + const char kBackslash = '\\'; + + result->push_back(kQuote); + size_t consecutive_backslash_count = 0; + string::const_iterator span_begin = input.begin(); + for (string::const_iterator it = input.begin(), end = input.end(); it != end; + ++it) { + switch (*it) { + case kBackslash: + ++consecutive_backslash_count; + break; + case kQuote: + result->append(span_begin, it); + result->append(consecutive_backslash_count + 1, kBackslash); + span_begin = it; + consecutive_backslash_count = 0; + break; + default: + consecutive_backslash_count = 0; + break; + } + } + result->append(span_begin, input.end()); + result->append(consecutive_backslash_count, kBackslash); + result->push_back(kQuote); +} + +int ReadFile(const string& path, string* contents, string* err) { +#ifdef _WIN32 + // This makes a ninja run on a set of 1500 manifest files about 4% faster + // than using the generic fopen code below. + err->clear(); + HANDLE f = ::CreateFile(path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (f == INVALID_HANDLE_VALUE) { + err->assign(GetLastErrorString()); + return -ENOENT; + } + + for (;;) { + DWORD len; + char buf[64 << 10]; + if (!::ReadFile(f, buf, sizeof(buf), &len, NULL)) { + err->assign(GetLastErrorString()); + contents->clear(); + return -1; + } + if (len == 0) + break; + contents->append(buf, len); + } + ::CloseHandle(f); + return 0; +#else + FILE* f = fopen(path.c_str(), "rb"); + if (!f) { + err->assign(strerror(errno)); + return -errno; + } + + char buf[64 << 10]; + size_t len; + while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { + contents->append(buf, len); + } + if (ferror(f)) { + err->assign(strerror(errno)); // XXX errno? + contents->clear(); + fclose(f); + return -errno; + } + fclose(f); + return 0; +#endif +} + +void SetCloseOnExec(int fd) { +#ifndef _WIN32 + int flags = fcntl(fd, F_GETFD); + if (flags < 0) { + perror("fcntl(F_GETFD)"); + } else { + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) + perror("fcntl(F_SETFD)"); + } +#else + HANDLE hd = (HANDLE) _get_osfhandle(fd); + if (! SetHandleInformation(hd, HANDLE_FLAG_INHERIT, 0)) { + fprintf(stderr, "SetHandleInformation(): %s", GetLastErrorString().c_str()); + } +#endif // ! _WIN32 +} + + +const char* SpellcheckStringV(const string& text, + const vector& words) { + const bool kAllowReplacements = true; + const int kMaxValidEditDistance = 3; + + int min_distance = kMaxValidEditDistance + 1; + const char* result = NULL; + for (vector::const_iterator i = words.begin(); + i != words.end(); ++i) { + int distance = EditDistance(*i, text, kAllowReplacements, + kMaxValidEditDistance); + if (distance < min_distance) { + min_distance = distance; + result = *i; + } + } + return result; +} + +const char* SpellcheckString(const char* text, ...) { + // Note: This takes a const char* instead of a string& because using + // va_start() with a reference parameter is undefined behavior. + va_list ap; + va_start(ap, text); + vector words; + const char* word; + while ((word = va_arg(ap, const char*))) + words.push_back(word); + va_end(ap); + return SpellcheckStringV(text, words); +} + +#ifdef _WIN32 +string GetLastErrorString() { + DWORD err = GetLastError(); + + char* msg_buf; + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&msg_buf, + 0, + NULL); + string msg = msg_buf; + LocalFree(msg_buf); + return msg; +} + +void Win32Fatal(const char* function) { + Fatal("%s: %s", function, GetLastErrorString().c_str()); +} +#endif + +bool islatinalpha(int c) { + // isalpha() is locale-dependent. + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +string StripAnsiEscapeCodes(const string& in) { + string stripped; + stripped.reserve(in.size()); + + for (size_t i = 0; i < in.size(); ++i) { + if (in[i] != '\33') { + // Not an escape code. + stripped.push_back(in[i]); + continue; + } + + // Only strip CSIs for now. + if (i + 1 >= in.size()) break; + if (in[i + 1] != '[') continue; // Not a CSI. + i += 2; + + // Skip everything up to and including the next [a-zA-Z]. + while (i < in.size() && !islatinalpha(in[i])) + ++i; + } + return stripped; +} + +int GetProcessorCount() { +#ifdef _WIN32 + SYSTEM_INFO info; + GetNativeSystemInfo(&info); + return info.dwNumberOfProcessors; +#else + return sysconf(_SC_NPROCESSORS_ONLN); +#endif +} + +#if defined(_WIN32) || defined(__CYGWIN__) +static double CalculateProcessorLoad(uint64_t idle_ticks, uint64_t total_ticks) +{ + static uint64_t previous_idle_ticks = 0; + static uint64_t previous_total_ticks = 0; + static double previous_load = -0.0; + + uint64_t idle_ticks_since_last_time = idle_ticks - previous_idle_ticks; + uint64_t total_ticks_since_last_time = total_ticks - previous_total_ticks; + + bool first_call = (previous_total_ticks == 0); + bool ticks_not_updated_since_last_call = (total_ticks_since_last_time == 0); + + double load; + if (first_call || ticks_not_updated_since_last_call) { + load = previous_load; + } else { + // Calculate load. + double idle_to_total_ratio = + ((double)idle_ticks_since_last_time) / total_ticks_since_last_time; + double load_since_last_call = 1.0 - idle_to_total_ratio; + + // Filter/smooth result when possible. + if(previous_load > 0) { + load = 0.9 * previous_load + 0.1 * load_since_last_call; + } else { + load = load_since_last_call; + } + } + + previous_load = load; + previous_total_ticks = total_ticks; + previous_idle_ticks = idle_ticks; + + return load; +} + +static uint64_t FileTimeToTickCount(const FILETIME & ft) +{ + uint64_t high = (((uint64_t)(ft.dwHighDateTime)) << 32); + uint64_t low = ft.dwLowDateTime; + return (high | low); +} + +double GetLoadAverage() { + FILETIME idle_time, kernel_time, user_time; + BOOL get_system_time_succeeded = + GetSystemTimes(&idle_time, &kernel_time, &user_time); + + double posix_compatible_load; + if (get_system_time_succeeded) { + uint64_t idle_ticks = FileTimeToTickCount(idle_time); + + // kernel_time from GetSystemTimes already includes idle_time. + uint64_t total_ticks = + FileTimeToTickCount(kernel_time) + FileTimeToTickCount(user_time); + + double processor_load = CalculateProcessorLoad(idle_ticks, total_ticks); + posix_compatible_load = processor_load * GetProcessorCount(); + + } else { + posix_compatible_load = -0.0; + } + + return posix_compatible_load; +} +#elif defined(_AIX) +double GetLoadAverage() { + perfstat_cpu_total_t cpu_stats; + if (perfstat_cpu_total(NULL, &cpu_stats, sizeof(cpu_stats), 1) < 0) { + return -0.0f; + } + + // Calculation taken from comment in libperfstats.h + return double(cpu_stats.loadavg[0]) / double(1 << SBITS); +} +#elif defined(__UCLIBC__) +double GetLoadAverage() { + struct sysinfo si; + if (sysinfo(&si) != 0) + return -0.0f; + return 1.0 / (1 << SI_LOAD_SHIFT) * si.loads[0]; +} +#else +double GetLoadAverage() { + double loadavg[3] = { 0.0f, 0.0f, 0.0f }; + if (getloadavg(loadavg, 3) < 0) { + // Maybe we should return an error here or the availability of + // getloadavg(3) should be checked when ninja is configured. + return -0.0f; + } + return loadavg[0]; +} +#endif // _WIN32 + +string ElideMiddle(const string& str, size_t width) { + const int kMargin = 3; // Space for "...". + string result = str; + if (result.size() + kMargin > width) { + size_t elide_size = (width - kMargin) / 2; + result = result.substr(0, elide_size) + + "..." + + result.substr(result.size() - elide_size, elide_size); + } + return result; +} + +bool Truncate(const string& path, size_t size, string* err) { +#ifdef _WIN32 + int fh = _sopen(path.c_str(), _O_RDWR | _O_CREAT, _SH_DENYNO, + _S_IREAD | _S_IWRITE); + int success = _chsize(fh, size); + _close(fh); +#else + int success = truncate(path.c_str(), size); +#endif + // Both truncate() and _chsize() return 0 on success and set errno and return + // -1 on failure. + if (success < 0) { + *err = strerror(errno); + return false; + } + return true; +} diff --git a/src/3rdparty/ninja/src/util.h b/src/3rdparty/ninja/src/util.h new file mode 100644 index 00000000000..4ee41a500a8 --- /dev/null +++ b/src/3rdparty/ninja/src/util.h @@ -0,0 +1,111 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_UTIL_H_ +#define NINJA_UTIL_H_ + +#ifdef _WIN32 +#include "win32port.h" +#else +#include +#endif + +#include +#include +using namespace std; + +#ifdef _MSC_VER +#define NORETURN __declspec(noreturn) +#else +#define NORETURN __attribute__((noreturn)) +#endif + +/// Log a fatal message and exit. +NORETURN void Fatal(const char* msg, ...); + +/// Log a warning message. +void Warning(const char* msg, ...); + +/// Log an error message. +void Error(const char* msg, ...); + +/// Canonicalize a path like "foo/../bar.h" into just "bar.h". +/// |slash_bits| has bits set starting from lowest for a backslash that was +/// normalized to a forward slash. (only used on Windows) +bool CanonicalizePath(string* path, uint64_t* slash_bits, string* err); +bool CanonicalizePath(char* path, size_t* len, uint64_t* slash_bits, + string* err); + +/// Appends |input| to |*result|, escaping according to the whims of either +/// Bash, or Win32's CommandLineToArgvW(). +/// Appends the string directly to |result| without modification if we can +/// determine that it contains no problematic characters. +void GetShellEscapedString(const string& input, string* result); +void GetWin32EscapedString(const string& input, string* result); + +/// Read a file to a string (in text mode: with CRLF conversion +/// on Windows). +/// Returns -errno and fills in \a err on error. +int ReadFile(const string& path, string* contents, string* err); + +/// Mark a file descriptor to not be inherited on exec()s. +void SetCloseOnExec(int fd); + +/// Given a misspelled string and a list of correct spellings, returns +/// the closest match or NULL if there is no close enough match. +const char* SpellcheckStringV(const string& text, + const vector& words); + +/// Like SpellcheckStringV, but takes a NULL-terminated list. +const char* SpellcheckString(const char* text, ...); + +bool islatinalpha(int c); + +/// Removes all Ansi escape codes (http://www.termsys.demon.co.uk/vtansi.htm). +string StripAnsiEscapeCodes(const string& in); + +/// @return the number of processors on the machine. Useful for an initial +/// guess for how many jobs to run in parallel. @return 0 on error. +int GetProcessorCount(); + +/// @return the load average of the machine. A negative value is returned +/// on error. +double GetLoadAverage(); + +/// Elide the given string @a str with '...' in the middle if the length +/// exceeds @a width. +string ElideMiddle(const string& str, size_t width); + +/// Truncates a file to the given size. +bool Truncate(const string& path, size_t size, string* err); + +#ifdef _MSC_VER +#define snprintf _snprintf +#define fileno _fileno +#define unlink _unlink +#define chdir _chdir +#define strtoull _strtoui64 +#define getcwd _getcwd +#define PATH_MAX _MAX_PATH +#endif + +#ifdef _WIN32 +/// Convert the value returned by GetLastError() into a string. +string GetLastErrorString(); + +/// Calls Fatal() with a function name and GetLastErrorString. +NORETURN void Win32Fatal(const char* function); +#endif + +#endif // NINJA_UTIL_H_ diff --git a/src/3rdparty/ninja/src/util_test.cc b/src/3rdparty/ninja/src/util_test.cc new file mode 100644 index 00000000000..b4b75169d63 --- /dev/null +++ b/src/3rdparty/ninja/src/util_test.cc @@ -0,0 +1,428 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util.h" + +#include "test.h" + +namespace { + +bool CanonicalizePath(string* path, string* err) { + uint64_t unused; + return ::CanonicalizePath(path, &unused, err); +} + +} // namespace + +TEST(CanonicalizePath, PathSamples) { + string path; + string err; + + EXPECT_FALSE(CanonicalizePath(&path, &err)); + EXPECT_EQ("empty path", err); + + path = "foo.h"; err = ""; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo.h", path); + + path = "./foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo.h", path); + + path = "./foo/./bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo/bar.h", path); + + path = "./x/foo/../bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("x/bar.h", path); + + path = "./x/foo/../../bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("bar.h", path); + + path = "foo//bar"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo/bar", path); + + path = "foo//.//..///bar"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("bar", path); + + path = "./x/../foo/../../bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("../bar.h", path); + + path = "foo/./."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo", path); + + path = "foo/bar/.."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo", path); + + path = "foo/.hidden_bar"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo/.hidden_bar", path); + + path = "/foo"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("/foo", path); + + path = "//foo"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); +#ifdef _WIN32 + EXPECT_EQ("//foo", path); +#else + EXPECT_EQ("/foo", path); +#endif + + path = "/"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("", path); + + path = "/foo/.."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("", path); + + path = "."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ(".", path); + + path = "./."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ(".", path); + + path = "foo/.."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ(".", path); +} + +#ifdef _WIN32 +TEST(CanonicalizePath, PathSamplesWindows) { + string path; + string err; + + EXPECT_FALSE(CanonicalizePath(&path, &err)); + EXPECT_EQ("empty path", err); + + path = "foo.h"; err = ""; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo.h", path); + + path = ".\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo.h", path); + + path = ".\\foo\\.\\bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo/bar.h", path); + + path = ".\\x\\foo\\..\\bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("x/bar.h", path); + + path = ".\\x\\foo\\..\\..\\bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("bar.h", path); + + path = "foo\\\\bar"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo/bar", path); + + path = "foo\\\\.\\\\..\\\\\\bar"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("bar", path); + + path = ".\\x\\..\\foo\\..\\..\\bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("../bar.h", path); + + path = "foo\\.\\."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo", path); + + path = "foo\\bar\\.."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo", path); + + path = "foo\\.hidden_bar"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("foo/.hidden_bar", path); + + path = "\\foo"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("/foo", path); + + path = "\\\\foo"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("//foo", path); + + path = "\\"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("", path); +} + +TEST(CanonicalizePath, SlashTracking) { + string path; + string err; + uint64_t slash_bits; + + path = "foo.h"; err = ""; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("foo.h", path); + EXPECT_EQ(0, slash_bits); + + path = "a\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/foo.h", path); + EXPECT_EQ(1, slash_bits); + + path = "a/bcd/efh\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/bcd/efh/foo.h", path); + EXPECT_EQ(4, slash_bits); + + path = "a\\bcd/efh\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/bcd/efh/foo.h", path); + EXPECT_EQ(5, slash_bits); + + path = "a\\bcd\\efh\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/bcd/efh/foo.h", path); + EXPECT_EQ(7, slash_bits); + + path = "a/bcd/efh/foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/bcd/efh/foo.h", path); + EXPECT_EQ(0, slash_bits); + + path = "a\\./efh\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/efh/foo.h", path); + EXPECT_EQ(3, slash_bits); + + path = "a\\../efh\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("efh/foo.h", path); + EXPECT_EQ(1, slash_bits); + + path = "a\\b\\c\\d\\e\\f\\g\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/b/c/d/e/f/g/foo.h", path); + EXPECT_EQ(127, slash_bits); + + path = "a\\b\\c\\..\\..\\..\\g\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("g/foo.h", path); + EXPECT_EQ(1, slash_bits); + + path = "a\\b/c\\../../..\\g\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("g/foo.h", path); + EXPECT_EQ(1, slash_bits); + + path = "a\\b/c\\./../..\\g\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/g/foo.h", path); + EXPECT_EQ(3, slash_bits); + + path = "a\\b/c\\./../..\\g/foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/g/foo.h", path); + EXPECT_EQ(1, slash_bits); + + path = "a\\\\\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/foo.h", path); + EXPECT_EQ(1, slash_bits); + + path = "a/\\\\foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/foo.h", path); + EXPECT_EQ(0, slash_bits); + + path = "a\\//foo.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ("a/foo.h", path); + EXPECT_EQ(1, slash_bits); +} + +TEST(CanonicalizePath, CanonicalizeNotExceedingLen) { + // Make sure searching \/ doesn't go past supplied len. + char buf[] = "foo/bar\\baz.h\\"; // Last \ past end. + uint64_t slash_bits; + string err; + size_t size = 13; + EXPECT_TRUE(::CanonicalizePath(buf, &size, &slash_bits, &err)); + EXPECT_EQ(0, strncmp("foo/bar/baz.h", buf, size)); + EXPECT_EQ(2, slash_bits); // Not including the trailing one. +} + +TEST(CanonicalizePath, TooManyComponents) { + string path; + string err; + uint64_t slash_bits; + + // 64 is OK. + 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/./x.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ(slash_bits, 0x0); + + // Backslashes version. + 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\\.\\x.h"; + + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ(slash_bits, 0xffffffff); + + // 65 is OK if #component is less than 60 after path canonicalization. + err = ""; + 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/./x/y.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ(slash_bits, 0x0); + + // Backslashes version. + err = ""; + 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\\.\\x\\y.h"; + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ(slash_bits, 0x1ffffffff); + + + // 59 after canonicalization is OK. + err = ""; + 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/" + "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"; + EXPECT_EQ(58, std::count(path.begin(), path.end(), '/')); + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ(slash_bits, 0x0); + + // Backslashes version. + err = ""; + 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\\" + "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"; + EXPECT_EQ(58, std::count(path.begin(), path.end(), '\\')); + EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err)); + EXPECT_EQ(slash_bits, 0x3ffffffffffffff); +} +#endif + +TEST(CanonicalizePath, UpDir) { + string path, err; + path = "../../foo/bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("../../foo/bar.h", path); + + path = "test/../../foo/bar.h"; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("../foo/bar.h", path); +} + +TEST(CanonicalizePath, AbsolutePath) { + string path = "/usr/include/stdio.h"; + string err; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("/usr/include/stdio.h", path); +} + +TEST(CanonicalizePath, NotNullTerminated) { + string path; + string err; + size_t len; + uint64_t unused; + + path = "foo/. bar/."; + len = strlen("foo/."); // Canonicalize only the part before the space. + EXPECT_TRUE(CanonicalizePath(&path[0], &len, &unused, &err)); + EXPECT_EQ(strlen("foo"), len); + EXPECT_EQ("foo/. bar/.", string(path)); + + path = "foo/../file bar/."; + len = strlen("foo/../file"); + EXPECT_TRUE(CanonicalizePath(&path[0], &len, &unused, &err)); + EXPECT_EQ(strlen("file"), len); + EXPECT_EQ("file ./file bar/.", string(path)); +} + +TEST(PathEscaping, TortureTest) { + string result; + + GetWin32EscapedString("foo bar\\\"'$@d!st!c'\\path'\\", &result); + EXPECT_EQ("\"foo bar\\\\\\\"'$@d!st!c'\\path'\\\\\"", result); + result.clear(); + + GetShellEscapedString("foo bar\"/'$@d!st!c'/path'", &result); + EXPECT_EQ("'foo bar\"/'\\''$@d!st!c'\\''/path'\\'''", result); +} + +TEST(PathEscaping, SensiblePathsAreNotNeedlesslyEscaped) { + const char* path = "some/sensible/path/without/crazy/characters.c++"; + string result; + + GetWin32EscapedString(path, &result); + EXPECT_EQ(path, result); + result.clear(); + + GetShellEscapedString(path, &result); + EXPECT_EQ(path, result); +} + +TEST(PathEscaping, SensibleWin32PathsAreNotNeedlesslyEscaped) { + const char* path = "some\\sensible\\path\\without\\crazy\\characters.c++"; + string result; + + GetWin32EscapedString(path, &result); + EXPECT_EQ(path, result); +} + +TEST(StripAnsiEscapeCodes, EscapeAtEnd) { + string stripped = StripAnsiEscapeCodes("foo\33"); + EXPECT_EQ("foo", stripped); + + stripped = StripAnsiEscapeCodes("foo\33["); + EXPECT_EQ("foo", stripped); +} + +TEST(StripAnsiEscapeCodes, StripColors) { + // An actual clang warning. + string input = "\33[1maffixmgr.cxx:286:15: \33[0m\33[0;1;35mwarning: " + "\33[0m\33[1musing the result... [-Wparentheses]\33[0m"; + string stripped = StripAnsiEscapeCodes(input); + EXPECT_EQ("affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]", + stripped); +} + +TEST(ElideMiddle, NothingToElide) { + string input = "Nothing to elide in this short string."; + EXPECT_EQ(input, ElideMiddle(input, 80)); +} + +TEST(ElideMiddle, ElideInTheMiddle) { + string input = "01234567890123456789"; + string elided = ElideMiddle(input, 10); + EXPECT_EQ("012...789", elided); +} diff --git a/src/3rdparty/ninja/src/version.cc b/src/3rdparty/ninja/src/version.cc new file mode 100644 index 00000000000..3a20205cd88 --- /dev/null +++ b/src/3rdparty/ninja/src/version.cc @@ -0,0 +1,53 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "version.h" + +#include + +#include "util.h" + +const char* kNinjaVersion = "1.8.2"; + +void ParseVersion(const string& version, int* major, int* minor) { + size_t end = version.find('.'); + *major = atoi(version.substr(0, end).c_str()); + *minor = 0; + if (end != string::npos) { + size_t start = end + 1; + end = version.find('.', start); + *minor = atoi(version.substr(start, end).c_str()); + } +} + +void CheckNinjaVersion(const string& version) { + int bin_major, bin_minor; + ParseVersion(kNinjaVersion, &bin_major, &bin_minor); + int file_major, file_minor; + ParseVersion(version, &file_major, &file_minor); + + if (bin_major > file_major) { + Warning("ninja executable version (%s) greater than build file " + "ninja_required_version (%s); versions may be incompatible.", + kNinjaVersion, version.c_str()); + return; + } + + if ((bin_major == file_major && bin_minor < file_minor) || + bin_major < file_major) { + Fatal("ninja version (%s) incompatible with build file " + "ninja_required_version version (%s).", + kNinjaVersion, version.c_str()); + } +} diff --git a/src/3rdparty/ninja/src/version.h b/src/3rdparty/ninja/src/version.h new file mode 100644 index 00000000000..bd6b9ffe21e --- /dev/null +++ b/src/3rdparty/ninja/src/version.h @@ -0,0 +1,32 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_VERSION_H_ +#define NINJA_VERSION_H_ + +#include +using namespace std; + +/// The version number of the current Ninja release. This will always +/// be "git" on trunk. +extern const char* kNinjaVersion; + +/// Parse the major/minor components of a version string. +void ParseVersion(const string& version, int* major, int* minor); + +/// Check whether \a version is compatible with the current Ninja version, +/// aborting if not. +void CheckNinjaVersion(const string& required_version); + +#endif // NINJA_VERSION_H_ diff --git a/src/3rdparty/ninja/src/win32port.h b/src/3rdparty/ninja/src/win32port.h new file mode 100644 index 00000000000..ce3c9498e5d --- /dev/null +++ b/src/3rdparty/ninja/src/win32port.h @@ -0,0 +1,31 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_WIN32PORT_H_ +#define NINJA_WIN32PORT_H_ + +typedef signed short int16_t; +typedef unsigned short uint16_t; +/// A 64-bit integer type +typedef signed long long int64_t; +typedef unsigned long long uint64_t; + +// printf format specifier for uint64_t, from C99. +#ifndef PRIu64 +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#endif + +#endif // NINJA_WIN32PORT_H_ + From 92003aa9c00677b30c0100dc9b0ab0b44f16a699 Mon Sep 17 00:00:00 2001 From: Patricia Aas Date: Thu, 7 Jan 2021 11:55:32 +0100 Subject: [PATCH 04/51] Add gn --- src/3rdparty/gn/.clang-format | 2 + src/3rdparty/gn/.editorconfig | 3 + src/3rdparty/gn/.style.yapf | 2 + src/3rdparty/gn/AUTHORS | 45 + src/3rdparty/gn/LICENSE | 27 + src/3rdparty/gn/OWNERS | 3 + src/3rdparty/gn/README.md | 90 + src/3rdparty/gn/base/atomic_ref_count.h | 67 + src/3rdparty/gn/base/bind.h | 457 + src/3rdparty/gn/base/bind_internal.h | 912 ++ src/3rdparty/gn/base/callback.h | 142 + src/3rdparty/gn/base/callback_forward.h | 27 + src/3rdparty/gn/base/callback_internal.cc | 93 + src/3rdparty/gn/base/callback_internal.h | 172 + src/3rdparty/gn/base/command_line.cc | 486 ++ src/3rdparty/gn/base/command_line.h | 255 + src/3rdparty/gn/base/compiler_specific.h | 231 + .../gn/base/containers/circular_deque.h | 1111 +++ src/3rdparty/gn/base/containers/flat_map.h | 362 + src/3rdparty/gn/base/containers/flat_set.h | 141 + src/3rdparty/gn/base/containers/flat_tree.h | 1004 +++ src/3rdparty/gn/base/containers/queue.h | 23 + src/3rdparty/gn/base/containers/span.h | 453 + src/3rdparty/gn/base/containers/stack.h | 23 + .../gn/base/containers/vector_buffer.h | 163 + src/3rdparty/gn/base/environment.cc | 237 + src/3rdparty/gn/base/environment.h | 86 + src/3rdparty/gn/base/files/file.cc | 132 + src/3rdparty/gn/base/files/file.h | 356 + src/3rdparty/gn/base/files/file_enumerator.cc | 25 + src/3rdparty/gn/base/files/file_enumerator.h | 171 + .../gn/base/files/file_enumerator_posix.cc | 170 + .../gn/base/files/file_enumerator_win.cc | 192 + src/3rdparty/gn/base/files/file_path.cc | 669 ++ src/3rdparty/gn/base/files/file_path.h | 426 + .../gn/base/files/file_path_constants.cc | 25 + src/3rdparty/gn/base/files/file_posix.cc | 434 + src/3rdparty/gn/base/files/file_util.cc | 263 + src/3rdparty/gn/base/files/file_util.h | 395 + src/3rdparty/gn/base/files/file_util_linux.cc | 63 + src/3rdparty/gn/base/files/file_util_posix.cc | 787 ++ src/3rdparty/gn/base/files/file_util_win.cc | 697 ++ src/3rdparty/gn/base/files/file_win.cc | 381 + src/3rdparty/gn/base/files/platform_file.h | 43 + src/3rdparty/gn/base/files/scoped_file.cc | 49 + src/3rdparty/gn/base/files/scoped_file.h | 59 + src/3rdparty/gn/base/files/scoped_temp_dir.cc | 97 + src/3rdparty/gn/base/files/scoped_temp_dir.h | 70 + src/3rdparty/gn/base/gtest_prod_util.h | 16 + src/3rdparty/gn/base/json/json_parser.cc | 747 ++ src/3rdparty/gn/base/json/json_parser.h | 260 + src/3rdparty/gn/base/json/json_reader.cc | 119 + src/3rdparty/gn/base/json/json_reader.h | 134 + .../gn/base/json/json_value_converter.cc | 36 + .../gn/base/json/json_value_converter.h | 512 ++ src/3rdparty/gn/base/json/json_writer.cc | 177 + src/3rdparty/gn/base/json/json_writer.h | 74 + src/3rdparty/gn/base/json/string_escape.cc | 167 + src/3rdparty/gn/base/json/string_escape.h | 55 + src/3rdparty/gn/base/logging.cc | 330 + src/3rdparty/gn/base/logging.h | 956 +++ src/3rdparty/gn/base/mac/bundle_locations.h | 65 + src/3rdparty/gn/base/mac/mac_logging.h | 74 + src/3rdparty/gn/base/mac/mac_logging.mm | 42 + src/3rdparty/gn/base/mac/scoped_cftyperef.h | 48 + src/3rdparty/gn/base/mac/scoped_typeref.h | 138 + src/3rdparty/gn/base/macros.h | 94 + src/3rdparty/gn/base/md5.cc | 299 + src/3rdparty/gn/base/md5.h | 76 + src/3rdparty/gn/base/memory/free_deleter.h | 23 + src/3rdparty/gn/base/memory/ptr_util.h | 23 + .../raw_scoped_refptr_mismatch_checker.h | 52 + src/3rdparty/gn/base/memory/ref_counted.cc | 35 + src/3rdparty/gn/base/memory/ref_counted.h | 317 + src/3rdparty/gn/base/memory/scoped_policy.h | 25 + src/3rdparty/gn/base/memory/scoped_refptr.h | 333 + src/3rdparty/gn/base/memory/weak_ptr.cc | 75 + src/3rdparty/gn/base/memory/weak_ptr.h | 378 + src/3rdparty/gn/base/numerics/checked_math.h | 393 + .../gn/base/numerics/checked_math_impl.h | 567 ++ src/3rdparty/gn/base/numerics/clamped_math.h | 262 + .../gn/base/numerics/clamped_math_impl.h | 341 + .../gn/base/numerics/math_constants.h | 15 + src/3rdparty/gn/base/numerics/ranges.h | 27 + .../gn/base/numerics/safe_conversions.h | 339 + .../base/numerics/safe_conversions_arm_impl.h | 51 + .../gn/base/numerics/safe_conversions_impl.h | 848 ++ src/3rdparty/gn/base/numerics/safe_math.h | 12 + .../gn/base/numerics/safe_math_arm_impl.h | 122 + .../base/numerics/safe_math_clang_gcc_impl.h | 152 + .../gn/base/numerics/safe_math_shared_impl.h | 237 + src/3rdparty/gn/base/optional.h | 922 ++ src/3rdparty/gn/base/posix/eintr_wrapper.h | 71 + .../gn/base/posix/file_descriptor_shuffle.cc | 101 + .../gn/base/posix/file_descriptor_shuffle.h | 82 + src/3rdparty/gn/base/posix/safe_strerror.cc | 126 + src/3rdparty/gn/base/posix/safe_strerror.h | 42 + src/3rdparty/gn/base/scoped_clear_errno.h | 32 + src/3rdparty/gn/base/scoped_generic.h | 185 + src/3rdparty/gn/base/sha1.cc | 213 + src/3rdparty/gn/base/sha1.h | 28 + src/3rdparty/gn/base/stl_util.h | 406 + src/3rdparty/gn/base/strings/char_traits.h | 92 + src/3rdparty/gn/base/strings/string16.cc | 87 + src/3rdparty/gn/base/strings/string16.h | 203 + .../base/strings/string_number_conversions.cc | 458 + .../base/strings/string_number_conversions.h | 152 + src/3rdparty/gn/base/strings/string_piece.cc | 440 + src/3rdparty/gn/base/strings/string_piece.h | 439 + .../gn/base/strings/string_piece_forward.h | 24 + src/3rdparty/gn/base/strings/string_split.cc | 268 + src/3rdparty/gn/base/strings/string_split.h | 122 + .../gn/base/strings/string_tokenizer.h | 251 + src/3rdparty/gn/base/strings/string_util.cc | 1101 +++ src/3rdparty/gn/base/strings/string_util.h | 455 + .../gn/base/strings/string_util_constants.cc | 59 + .../gn/base/strings/string_util_posix.h | 41 + .../gn/base/strings/string_util_win.h | 48 + .../gn/base/strings/stringize_macros.h | 31 + src/3rdparty/gn/base/strings/stringprintf.cc | 190 + src/3rdparty/gn/base/strings/stringprintf.h | 60 + .../strings/utf_offset_string_conversions.cc | 266 + .../strings/utf_offset_string_conversions.h | 111 + .../strings/utf_string_conversion_utils.cc | 153 + .../strings/utf_string_conversion_utils.h | 99 + .../gn/base/strings/utf_string_conversions.cc | 333 + .../gn/base/strings/utf_string_conversions.h | 48 + src/3rdparty/gn/base/sys_byteorder.h | 139 + src/3rdparty/gn/base/template_util.h | 156 + src/3rdparty/gn/base/third_party/icu/LICENSE | 76 + .../gn/base/third_party/icu/README.chromium | 17 + .../gn/base/third_party/icu/icu_utf.cc | 129 + .../gn/base/third_party/icu/icu_utf.h | 464 + src/3rdparty/gn/base/timer/elapsed_timer.cc | 25 + src/3rdparty/gn/base/timer/elapsed_timer.h | 32 + src/3rdparty/gn/base/value_iterators.cc | 228 + src/3rdparty/gn/base/value_iterators.h | 191 + src/3rdparty/gn/base/values.cc | 1297 +++ src/3rdparty/gn/base/values.h | 757 ++ src/3rdparty/gn/base/win/registry.cc | 613 ++ src/3rdparty/gn/base/win/registry.h | 248 + src/3rdparty/gn/base/win/scoped_handle.cc | 35 + src/3rdparty/gn/base/win/scoped_handle.h | 172 + .../gn/base/win/scoped_process_information.cc | 109 + .../gn/base/win/scoped_process_information.h | 74 + src/3rdparty/gn/base/win/windows_types.h | 254 + .../gn/build/build_aix.ninja.template | 16 + .../gn/build/build_linux.ninja.template | 19 + .../gn/build/build_mac.ninja.template | 19 + .../gn/build/build_openbsd.ninja.template | 19 + .../gn/build/build_win.ninja.template | 25 + src/3rdparty/gn/build/full_test.py | 81 + src/3rdparty/gn/build/gen.py | 746 ++ src/3rdparty/gn/docs/cross_compiles.md | 125 + src/3rdparty/gn/docs/faq.md | 58 + src/3rdparty/gn/docs/language.md | 540 ++ src/3rdparty/gn/docs/quick_start.md | 372 + src/3rdparty/gn/docs/reference.md | 7450 +++++++++++++++++ src/3rdparty/gn/docs/standalone.md | 44 + src/3rdparty/gn/docs/style_guide.md | 306 + src/3rdparty/gn/examples/rust_example/.gn | 1 + .../gn/examples/rust_example/BUILD.gn | 5 + .../gn/examples/rust_example/BUILDCONFIG.gn | 23 + .../gn/examples/rust_example/README.txt | 4 + .../gn/examples/rust_example/build/BUILD.gn | 27 + .../rust_example/hello_world/bar/src/BUILD.gn | 4 + .../rust_example/hello_world/bar/src/lib.rs | 23 + .../rust_example/hello_world/foo/src/BUILD.gn | 9 + .../rust_example/hello_world/foo/src/lib.rs | 11 + .../rust_example/hello_world/src/BUILD.gn | 12 + .../rust_example/hello_world/src/main.rs | 5 + src/3rdparty/gn/examples/simple_build/.gn | 2 + .../gn/examples/simple_build/BUILD.gn | 30 + .../gn/examples/simple_build/README.md | 12 + .../gn/examples/simple_build/build/BUILD.gn | 19 + .../simple_build/build/BUILDCONFIG.gn | 38 + .../simple_build/build/toolchain/BUILD.gn | 86 + .../gn/examples/simple_build/hello.cc | 13 + .../gn/examples/simple_build/hello_shared.cc | 9 + .../gn/examples/simple_build/hello_shared.h | 32 + .../gn/examples/simple_build/hello_static.cc | 9 + .../gn/examples/simple_build/hello_static.h | 10 + src/3rdparty/gn/infra/README.recipes.md | 108 + src/3rdparty/gn/infra/config/recipes.cfg | 12 + src/3rdparty/gn/infra/config/refs.cfg | 8 + .../recipe_modules/macos_sdk/__init__.py | 44 + .../gn/infra/recipe_modules/macos_sdk/api.py | 92 + .../examples/full.expected/linux.json | 22 + .../macos_sdk/examples/full.expected/mac.json | 82 + .../macos_sdk/examples/full.expected/win.json | 22 + .../recipe_modules/macos_sdk/examples/full.py | 22 + .../recipe_modules/windows_sdk/__init__.py | 31 + .../infra/recipe_modules/windows_sdk/api.py | 108 + .../examples/full.expected/linux.json | 22 + .../examples/full.expected/mac.json | 22 + .../examples/full.expected/win.json | 107 + .../windows_sdk/examples/full.py | 25 + src/3rdparty/gn/infra/recipes.py | 228 + .../infra/recipes/gn.expected/ci_linux.json | 230 + .../gn/infra/recipes/gn.expected/ci_mac.json | 296 + .../gn/infra/recipes/gn.expected/ci_win.json | 309 + .../recipes/gn.expected/cipd_exists.json | 267 + .../recipes/gn.expected/cipd_register.json | 283 + .../infra/recipes/gn.expected/cq_linux.json | 233 + .../gn/infra/recipes/gn.expected/cq_mac.json | 299 + .../gn/infra/recipes/gn.expected/cq_win.json | 312 + src/3rdparty/gn/infra/recipes/gn.py | 187 + .../gn/src/gn/function_filter_unittest.cc | 244 + .../gn/tools/gn/action_target_generator.cc | 221 + .../gn/tools/gn/action_target_generator.h | 41 + .../gn/action_target_generator_unittest.cc | 122 + src/3rdparty/gn/tools/gn/action_values.cc | 31 + src/3rdparty/gn/tools/gn/action_values.h | 70 + src/3rdparty/gn/tools/gn/analyzer.cc | 489 ++ src/3rdparty/gn/tools/gn/analyzer.h | 104 + src/3rdparty/gn/tools/gn/analyzer_unittest.cc | 597 ++ src/3rdparty/gn/tools/gn/args.cc | 422 + src/3rdparty/gn/tools/gn/args.h | 147 + src/3rdparty/gn/tools/gn/args_unittest.cc | 81 + .../gn/tools/gn/binary_target_generator.cc | 237 + .../gn/tools/gn/binary_target_generator.h | 41 + src/3rdparty/gn/tools/gn/build_settings.cc | 75 + src/3rdparty/gn/tools/gn/build_settings.h | 142 + src/3rdparty/gn/tools/gn/builder.cc | 602 ++ src/3rdparty/gn/tools/gn/builder.h | 146 + src/3rdparty/gn/tools/gn/builder_record.cc | 67 + src/3rdparty/gn/tools/gn/builder_record.h | 110 + src/3rdparty/gn/tools/gn/builder_unittest.cc | 244 + src/3rdparty/gn/tools/gn/bundle_data.cc | 180 + src/3rdparty/gn/tools/gn/bundle_data.h | 203 + .../tools/gn/bundle_data_target_generator.cc | 95 + .../tools/gn/bundle_data_target_generator.h | 32 + src/3rdparty/gn/tools/gn/bundle_file_rule.cc | 110 + src/3rdparty/gn/tools/gn/bundle_file_rule.h | 56 + .../gn/tools/gn/c_include_iterator.cc | 172 + src/3rdparty/gn/tools/gn/c_include_iterator.h | 57 + .../tools/gn/c_include_iterator_unittest.cc | 178 + .../gn/tools/gn/c_substitution_type.cc | 91 + .../gn/tools/gn/c_substitution_type.h | 46 + src/3rdparty/gn/tools/gn/c_tool.cc | 239 + src/3rdparty/gn/tools/gn/c_tool.h | 123 + src/3rdparty/gn/tools/gn/command_analyze.cc | 134 + src/3rdparty/gn/tools/gn/command_args.cc | 511 ++ src/3rdparty/gn/tools/gn/command_check.cc | 265 + src/3rdparty/gn/tools/gn/command_clean.cc | 130 + src/3rdparty/gn/tools/gn/command_desc.cc | 695 ++ src/3rdparty/gn/tools/gn/command_format.cc | 1246 +++ src/3rdparty/gn/tools/gn/command_format.h | 38 + .../gn/tools/gn/command_format_unittest.cc | 118 + src/3rdparty/gn/tools/gn/command_gen.cc | 531 ++ src/3rdparty/gn/tools/gn/command_help.cc | 379 + src/3rdparty/gn/tools/gn/command_ls.cc | 108 + src/3rdparty/gn/tools/gn/command_meta.cc | 170 + src/3rdparty/gn/tools/gn/command_path.cc | 413 + src/3rdparty/gn/tools/gn/command_refs.cc | 501 ++ src/3rdparty/gn/tools/gn/commands.cc | 559 ++ src/3rdparty/gn/tools/gn/commands.h | 210 + .../gn/tools/gn/compile_commands_writer.cc | 346 + .../gn/tools/gn/compile_commands_writer.h | 41 + .../gn/compile_commands_writer_unittest.cc | 647 ++ src/3rdparty/gn/tools/gn/config.cc | 50 + src/3rdparty/gn/tools/gn/config.h | 71 + src/3rdparty/gn/tools/gn/config_unittest.cc | 85 + src/3rdparty/gn/tools/gn/config_values.cc | 50 + src/3rdparty/gn/tools/gn/config_values.h | 98 + .../gn/tools/gn/config_values_extractors.cc | 34 + .../gn/tools/gn/config_values_extractors.h | 102 + .../gn/config_values_extractors_unittest.cc | 137 + .../gn/tools/gn/config_values_generator.cc | 150 + .../gn/tools/gn/config_values_generator.h | 46 + .../gn/tools/gn/copy_target_generator.cc | 44 + .../gn/tools/gn/copy_target_generator.h | 27 + .../gn/create_bundle_target_generator.cc | 303 + .../tools/gn/create_bundle_target_generator.h | 45 + src/3rdparty/gn/tools/gn/deps_iterator.cc | 53 + src/3rdparty/gn/tools/gn/deps_iterator.h | 72 + src/3rdparty/gn/tools/gn/desc_builder.cc | 838 ++ src/3rdparty/gn/tools/gn/desc_builder.h | 27 + src/3rdparty/gn/tools/gn/eclipse_writer.cc | 171 + src/3rdparty/gn/tools/gn/eclipse_writer.h | 67 + src/3rdparty/gn/tools/gn/err.cc | 192 + src/3rdparty/gn/tools/gn/err.h | 99 + src/3rdparty/gn/tools/gn/escape.cc | 300 + src/3rdparty/gn/tools/gn/escape.h | 80 + src/3rdparty/gn/tools/gn/escape_unittest.cc | 71 + src/3rdparty/gn/tools/gn/exec_process.cc | 281 + src/3rdparty/gn/tools/gn/exec_process.h | 36 + .../gn/tools/gn/exec_process_unittest.cc | 123 + src/3rdparty/gn/tools/gn/filesystem_utils.cc | 1115 +++ src/3rdparty/gn/tools/gn/filesystem_utils.h | 305 + .../gn/tools/gn/filesystem_utils_unittest.cc | 868 ++ .../gn/tools/gn/format_test_data/001.gn | 2 + .../gn/tools/gn/format_test_data/001.golden | 3 + .../gn/tools/gn/format_test_data/002.gn | 6 + .../gn/tools/gn/format_test_data/002.golden | 6 + .../gn/tools/gn/format_test_data/003.gn | 10 + .../gn/tools/gn/format_test_data/003.golden | 10 + .../gn/tools/gn/format_test_data/004.gn | 10 + .../gn/tools/gn/format_test_data/004.golden | 13 + .../gn/tools/gn/format_test_data/005.gn | 5 + .../gn/tools/gn/format_test_data/005.golden | 5 + .../gn/tools/gn/format_test_data/006.gn | 9 + .../gn/tools/gn/format_test_data/006.golden | 5 + .../gn/tools/gn/format_test_data/007.gn | 9 + .../gn/tools/gn/format_test_data/007.golden | 11 + .../gn/tools/gn/format_test_data/008.gn | 1 + .../gn/tools/gn/format_test_data/008.golden | 5 + .../gn/tools/gn/format_test_data/009.gn | 2 + .../gn/tools/gn/format_test_data/009.golden | 9 + .../gn/tools/gn/format_test_data/010.gn | 2 + .../gn/tools/gn/format_test_data/010.golden | 9 + .../gn/tools/gn/format_test_data/011.gn | 4 + .../gn/tools/gn/format_test_data/011.golden | 13 + .../gn/tools/gn/format_test_data/012.gn | 16 + .../gn/tools/gn/format_test_data/012.golden | 22 + .../gn/tools/gn/format_test_data/013.gn | 7 + .../gn/tools/gn/format_test_data/013.golden | 7 + .../gn/tools/gn/format_test_data/014.gn | 6 + .../gn/tools/gn/format_test_data/014.golden | 5 + .../gn/tools/gn/format_test_data/015.gn | 4 + .../gn/tools/gn/format_test_data/015.golden | 6 + .../gn/tools/gn/format_test_data/016.gn | 1 + .../gn/tools/gn/format_test_data/016.golden | 1 + .../gn/tools/gn/format_test_data/017.gn | 15 + .../gn/tools/gn/format_test_data/017.golden | 16 + .../gn/tools/gn/format_test_data/018.gn | 3 + .../gn/tools/gn/format_test_data/018.golden | 3 + .../gn/tools/gn/format_test_data/019.gn | 23 + .../gn/tools/gn/format_test_data/019.golden | 23 + .../gn/tools/gn/format_test_data/020.gn | 5 + .../gn/tools/gn/format_test_data/020.golden | 5 + .../gn/tools/gn/format_test_data/021.gn | 33 + .../gn/tools/gn/format_test_data/021.golden | 61 + .../gn/tools/gn/format_test_data/022.gn | 6 + .../gn/tools/gn/format_test_data/022.golden | 6 + .../gn/tools/gn/format_test_data/023.gn | 38 + .../gn/tools/gn/format_test_data/023.golden | 88 + .../gn/tools/gn/format_test_data/024.gn | 1 + .../gn/tools/gn/format_test_data/024.golden | 2 + .../gn/tools/gn/format_test_data/025.gn | 5 + .../gn/tools/gn/format_test_data/025.golden | 9 + .../gn/tools/gn/format_test_data/026.gn | 6 + .../gn/tools/gn/format_test_data/026.golden | 7 + .../gn/tools/gn/format_test_data/027.gn | 3 + .../gn/tools/gn/format_test_data/027.golden | 5 + .../gn/tools/gn/format_test_data/028.gn | 9 + .../gn/tools/gn/format_test_data/028.golden | 7 + .../gn/tools/gn/format_test_data/029.gn | 9 + .../gn/tools/gn/format_test_data/029.golden | 9 + .../gn/tools/gn/format_test_data/030.gn | 12 + .../gn/tools/gn/format_test_data/030.golden | 12 + .../gn/tools/gn/format_test_data/031.gn | 8 + .../gn/tools/gn/format_test_data/031.golden | 8 + .../gn/tools/gn/format_test_data/032.gn | 6 + .../gn/tools/gn/format_test_data/032.golden | 7 + .../gn/tools/gn/format_test_data/033.gn | 8 + .../gn/tools/gn/format_test_data/033.golden | 8 + .../gn/tools/gn/format_test_data/034.gn | 13 + .../gn/tools/gn/format_test_data/035.gn | 1 + .../gn/tools/gn/format_test_data/035.golden | 1 + .../gn/tools/gn/format_test_data/036.gn | 9 + .../gn/tools/gn/format_test_data/036.golden | 9 + .../gn/tools/gn/format_test_data/037.gn | 5 + .../gn/tools/gn/format_test_data/037.golden | 6 + .../gn/tools/gn/format_test_data/038.gn | 4 + .../gn/tools/gn/format_test_data/038.golden | 3 + .../gn/tools/gn/format_test_data/039.gn | 6 + .../gn/tools/gn/format_test_data/039.golden | 4 + .../gn/tools/gn/format_test_data/040.gn | 9 + .../gn/tools/gn/format_test_data/041.gn | 12 + .../gn/tools/gn/format_test_data/041.golden | 12 + .../gn/tools/gn/format_test_data/042.gn | 44 + .../gn/tools/gn/format_test_data/042.golden | 110 + .../gn/tools/gn/format_test_data/043.gn | 6 + .../gn/tools/gn/format_test_data/043.golden | 7 + .../gn/tools/gn/format_test_data/044.gn | 10 + .../gn/tools/gn/format_test_data/044.golden | 11 + .../gn/tools/gn/format_test_data/045.gn | 10 + .../gn/tools/gn/format_test_data/045.golden | 14 + .../gn/tools/gn/format_test_data/046.gn | 22 + .../gn/tools/gn/format_test_data/046.golden | 19 + .../gn/tools/gn/format_test_data/047.gn | 7 + .../gn/tools/gn/format_test_data/047.golden | 10 + .../gn/tools/gn/format_test_data/048.gn | 19 + .../gn/tools/gn/format_test_data/048.golden | 19 + .../gn/tools/gn/format_test_data/049.gn | 14 + .../gn/tools/gn/format_test_data/050.gn | 10 + .../gn/tools/gn/format_test_data/050.golden | 27 + .../gn/tools/gn/format_test_data/051.gn | 6 + .../gn/tools/gn/format_test_data/051.golden | 7 + .../gn/tools/gn/format_test_data/052.gn | 11 + .../gn/tools/gn/format_test_data/052.golden | 12 + .../gn/tools/gn/format_test_data/053.gn | 7 + .../gn/tools/gn/format_test_data/053.golden | 8 + .../gn/tools/gn/format_test_data/054.gn | 7 + .../gn/tools/gn/format_test_data/054.golden | 8 + .../gn/tools/gn/format_test_data/055.gn | 10 + .../gn/tools/gn/format_test_data/055.golden | 11 + .../gn/tools/gn/format_test_data/056.gn | 45 + .../gn/tools/gn/format_test_data/056.golden | 45 + .../gn/tools/gn/format_test_data/057.gn | 24 + .../gn/tools/gn/format_test_data/057.golden | 24 + .../gn/tools/gn/format_test_data/058.gn | 2 + .../gn/tools/gn/format_test_data/058.golden | 2 + .../gn/tools/gn/format_test_data/059.gn | 10 + .../gn/tools/gn/format_test_data/059.golden | 11 + .../gn/tools/gn/format_test_data/060.gn | 2 + .../gn/tools/gn/format_test_data/060.golden | 2 + .../gn/tools/gn/format_test_data/061.gn | 9 + .../gn/tools/gn/format_test_data/061.golden | 9 + .../gn/tools/gn/format_test_data/062.gn | 122 + .../gn/tools/gn/format_test_data/062.golden | 132 + .../gn/tools/gn/format_test_data/063.gn | 36 + .../gn/tools/gn/format_test_data/063.golden | 36 + .../gn/tools/gn/format_test_data/064.gn | 3 + .../gn/tools/gn/format_test_data/064.golden | 5 + .../gn/tools/gn/format_test_data/065.gn | 4 + .../gn/tools/gn/format_test_data/065.golden | 8 + .../gn/tools/gn/format_test_data/066.gn | 32 + .../gn/tools/gn/format_test_data/066.golden | 30 + .../gn/tools/gn/format_test_data/067.gn | 8 + .../gn/tools/gn/format_test_data/067.golden | 17 + .../gn/tools/gn/format_test_data/068.gn | 3 + .../gn/tools/gn/format_test_data/068.golden | 3 + .../gn/tools/gn/format_test_data/069.gn | 3 + .../gn/tools/gn/format_test_data/069.golden | 5 + .../gn/tools/gn/format_test_data/070.gn | 15 + .../gn/tools/gn/format_test_data/070.golden | 14 + .../gn/tools/gn/format_test_data/071.gn | 26 + .../gn/tools/gn/format_test_data/071.golden | 25 + .../gn/tools/gn/format_test_data/072.gn | 8 + .../gn/tools/gn/format_test_data/072.golden | 21 + .../gn/tools/gn/format_test_data/073.gn | 23 + .../gn/tools/gn/format_test_data/073.golden | 34 + .../gn/tools/gn/format_test_data/074.gn | 7 + .../gn/tools/gn/format_test_data/074.golden | 7 + .../gn/tools/gn/format_test_data/075.gn | 11 + .../gn/tools/gn/format_test_data/075.golden | 12 + src/3rdparty/gn/tools/gn/frameworks_utils.cc | 25 + src/3rdparty/gn/tools/gn/frameworks_utils.h | 16 + .../gn/tools/gn/frameworks_utils_unittest.cc | 17 + .../gn/tools/gn/function_exec_script.cc | 271 + src/3rdparty/gn/tools/gn/function_filter.cc | 124 + src/3rdparty/gn/tools/gn/function_foreach.cc | 112 + .../gn/tools/gn/function_foreach_unittest.cc | 100 + .../gn/function_forward_variables_from.cc | 249 + ...unction_forward_variables_from_unittest.cc | 244 + .../gn/tools/gn/function_get_label_info.cc | 142 + .../gn/function_get_label_info_unittest.cc | 107 + .../gn/tools/gn/function_get_path_info.cc | 251 + .../gn/function_get_path_info_unittest.cc | 120 + .../tools/gn/function_get_target_outputs.cc | 139 + .../function_get_target_outputs_unittest.cc | 107 + .../gn/function_process_file_template.cc | 115 + ...function_process_file_template_unittest.cc | 64 + .../gn/tools/gn/function_read_file.cc | 78 + .../gn/tools/gn/function_rebase_path.cc | 296 + .../tools/gn/function_rebase_path_unittest.cc | 183 + .../gn/function_set_default_toolchain.cc | 87 + .../gn/tools/gn/function_set_defaults.cc | 76 + src/3rdparty/gn/tools/gn/function_template.cc | 226 + .../gn/tools/gn/function_template_unittest.cc | 29 + .../gn/tools/gn/function_toolchain.cc | 857 ++ .../tools/gn/function_toolchain_unittest.cc | 119 + .../gn/tools/gn/function_write_file.cc | 107 + .../tools/gn/function_write_file_unittest.cc | 107 + src/3rdparty/gn/tools/gn/functions.cc | 1528 ++++ src/3rdparty/gn/tools/gn/functions.h | 554 ++ src/3rdparty/gn/tools/gn/functions_target.cc | 984 +++ .../gn/functions_target_rust_unittest.cc | 373 + .../gn/tools/gn/functions_target_unittest.cc | 206 + .../gn/tools/gn/functions_unittest.cc | 391 + src/3rdparty/gn/tools/gn/general_tool.cc | 51 + src/3rdparty/gn/tools/gn/general_tool.h | 46 + .../gn/generated_file_target_generator.cc | 165 + .../gn/generated_file_target_generator.h | 49 + src/3rdparty/gn/tools/gn/gn_main.cc | 85 + .../gn/tools/gn/group_target_generator.cc | 23 + .../gn/tools/gn/group_target_generator.h | 27 + src/3rdparty/gn/tools/gn/header_checker.cc | 625 ++ src/3rdparty/gn/tools/gn/header_checker.h | 206 + .../gn/tools/gn/header_checker_unittest.cc | 403 + src/3rdparty/gn/tools/gn/import_manager.cc | 162 + src/3rdparty/gn/tools/gn/import_manager.h | 54 + .../gn/tools/gn/inherited_libraries.cc | 74 + .../gn/tools/gn/inherited_libraries.h | 71 + .../tools/gn/inherited_libraries_unittest.cc | 135 + src/3rdparty/gn/tools/gn/input_conversion.cc | 359 + src/3rdparty/gn/tools/gn/input_conversion.h | 30 + .../gn/tools/gn/input_conversion_unittest.cc | 275 + src/3rdparty/gn/tools/gn/input_file.cc | 26 + src/3rdparty/gn/tools/gn/input_file.h | 65 + .../gn/tools/gn/input_file_manager.cc | 332 + src/3rdparty/gn/tools/gn/input_file_manager.h | 155 + src/3rdparty/gn/tools/gn/item.cc | 60 + src/3rdparty/gn/tools/gn/item.h | 82 + .../gn/tools/gn/json_project_writer.cc | 220 + .../gn/tools/gn/json_project_writer.h | 33 + .../tools/gn/json_project_writer_unittest.cc | 137 + src/3rdparty/gn/tools/gn/label.cc | 323 + src/3rdparty/gn/tools/gn/label.h | 109 + src/3rdparty/gn/tools/gn/label_pattern.cc | 276 + src/3rdparty/gn/tools/gn/label_pattern.h | 80 + .../gn/tools/gn/label_pattern_unittest.cc | 86 + src/3rdparty/gn/tools/gn/label_ptr.h | 79 + src/3rdparty/gn/tools/gn/label_unittest.cc | 95 + src/3rdparty/gn/tools/gn/lib_file.cc | 23 + src/3rdparty/gn/tools/gn/lib_file.h | 54 + src/3rdparty/gn/tools/gn/loader.cc | 437 + src/3rdparty/gn/tools/gn/loader.h | 178 + src/3rdparty/gn/tools/gn/loader_unittest.cc | 262 + src/3rdparty/gn/tools/gn/location.cc | 69 + src/3rdparty/gn/tools/gn/location.h | 60 + src/3rdparty/gn/tools/gn/metadata.cc | 271 + src/3rdparty/gn/tools/gn/metadata.h | 90 + src/3rdparty/gn/tools/gn/metadata_unittest.cc | 233 + src/3rdparty/gn/tools/gn/metadata_walk.cc | 24 + src/3rdparty/gn/tools/gn/metadata_walk.h | 26 + .../gn/tools/gn/metadata_walk_unittest.cc | 211 + .../gn/tools/gn/misc/emacs/gn-mode.el | 192 + src/3rdparty/gn/tools/gn/misc/help_as_html.py | 105 + .../gn/tools/gn/misc/tm/GN.tmLanguage | 102 + .../gn/tools/gn/misc/tm/GN.tmPreferences | 22 + src/3rdparty/gn/tools/gn/misc/vim/README.md | 29 + .../gn/tools/gn/misc/vim/autoload/gn.vim | 26 + .../tools/gn/misc/vim/ftdetect/gnfiletype.vim | 27 + .../gn/tools/gn/misc/vim/ftplugin/gn.vim | 12 + .../gn/tools/gn/misc/vim/gn-format.py | 59 + .../gn/tools/gn/misc/vim/syntax/gn.vim | 85 + .../gn/tools/gn/ninja_action_target_writer.cc | 239 + .../gn/tools/gn/ninja_action_target_writer.h | 62 + .../gn/ninja_action_target_writer_unittest.cc | 475 ++ .../gn/tools/gn/ninja_binary_target_writer.cc | 321 + .../gn/tools/gn/ninja_binary_target_writer.h | 79 + .../gn/ninja_binary_target_writer_unittest.cc | 108 + .../gn/tools/gn/ninja_build_writer.cc | 626 ++ src/3rdparty/gn/tools/gn/ninja_build_writer.h | 81 + .../tools/gn/ninja_build_writer_unittest.cc | 255 + .../gn/ninja_bundle_data_target_writer.cc | 33 + .../gn/ninja_bundle_data_target_writer.h | 23 + ...inja_bundle_data_target_writer_unittest.cc | 56 + .../tools/gn/ninja_c_binary_target_writer.cc | 697 ++ .../tools/gn/ninja_c_binary_target_writer.h | 105 + .../ninja_c_binary_target_writer_unittest.cc | 1251 +++ .../gn/tools/gn/ninja_copy_target_writer.cc | 118 + .../gn/tools/gn/ninja_copy_target_writer.h | 27 + .../gn/ninja_copy_target_writer_unittest.cc | 94 + .../gn/ninja_create_bundle_target_writer.cc | 357 + .../gn/ninja_create_bundle_target_writer.h | 71 + ...ja_create_bundle_target_writer_unittest.cc | 471 ++ .../gn/ninja_generated_file_target_writer.cc | 92 + .../gn/ninja_generated_file_target_writer.h | 25 + ...a_generated_file_target_writer_unittest.cc | 60 + .../gn/tools/gn/ninja_group_target_writer.cc | 32 + .../gn/tools/gn/ninja_group_target_writer.h | 23 + .../gn/ninja_group_target_writer_unittest.cc | 51 + .../gn/ninja_rust_binary_target_writer.cc | 270 + .../gn/ninja_rust_binary_target_writer.h | 37 + ...inja_rust_binary_target_writer_unittest.cc | 578 ++ .../gn/tools/gn/ninja_target_command_util.cc | 177 + .../gn/tools/gn/ninja_target_command_util.h | 134 + .../gn/ninja_target_command_util_unittest.cc | 100 + .../gn/tools/gn/ninja_target_writer.cc | 330 + .../gn/tools/gn/ninja_target_writer.h | 72 + .../tools/gn/ninja_target_writer_unittest.cc | 163 + .../gn/tools/gn/ninja_toolchain_writer.cc | 142 + .../gn/tools/gn/ninja_toolchain_writer.h | 61 + .../gn/ninja_toolchain_writer_unittest.cc | 39 + src/3rdparty/gn/tools/gn/ninja_utils.cc | 30 + src/3rdparty/gn/tools/gn/ninja_utils.h | 25 + src/3rdparty/gn/tools/gn/ninja_writer.cc | 52 + src/3rdparty/gn/tools/gn/ninja_writer.h | 49 + src/3rdparty/gn/tools/gn/operators.cc | 785 ++ src/3rdparty/gn/tools/gn/operators.h | 25 + .../gn/tools/gn/operators_unittest.cc | 424 + src/3rdparty/gn/tools/gn/ordered_set.h | 63 + src/3rdparty/gn/tools/gn/output_conversion.cc | 178 + src/3rdparty/gn/tools/gn/output_conversion.h | 26 + .../gn/tools/gn/output_conversion_unittest.cc | 351 + src/3rdparty/gn/tools/gn/output_file.cc | 39 + src/3rdparty/gn/tools/gn/output_file.h | 63 + .../gn/tools/gn/parse_node_value_adapter.cc | 44 + .../gn/tools/gn/parse_node_value_adapter.h | 55 + src/3rdparty/gn/tools/gn/parse_tree.cc | 941 +++ src/3rdparty/gn/tools/gn/parse_tree.h | 567 ++ .../gn/tools/gn/parse_tree_unittest.cc | 255 + src/3rdparty/gn/tools/gn/parser.cc | 942 +++ src/3rdparty/gn/tools/gn/parser.h | 157 + src/3rdparty/gn/tools/gn/parser_unittest.cc | 735 ++ src/3rdparty/gn/tools/gn/path_output.cc | 169 + src/3rdparty/gn/tools/gn/path_output.h | 92 + .../gn/tools/gn/path_output_unittest.cc | 276 + src/3rdparty/gn/tools/gn/pattern.cc | 223 + src/3rdparty/gn/tools/gn/pattern.h | 92 + src/3rdparty/gn/tools/gn/pattern_unittest.cc | 64 + src/3rdparty/gn/tools/gn/pool.cc | 45 + src/3rdparty/gn/tools/gn/pool.h | 41 + src/3rdparty/gn/tools/gn/qmake_link_writer.cc | 262 + src/3rdparty/gn/tools/gn/qmake_link_writer.h | 63 + .../gn/tools/gn/qmake_link_writer_unittest.cc | 141 + src/3rdparty/gn/tools/gn/qt_creator_writer.cc | 302 + src/3rdparty/gn/tools/gn/qt_creator_writer.h | 56 + src/3rdparty/gn/tools/gn/runtime_deps.cc | 314 + src/3rdparty/gn/tools/gn/runtime_deps.h | 28 + .../gn/tools/gn/runtime_deps_unittest.cc | 448 + .../gn/tools/gn/rust_substitution_type.cc | 48 + .../gn/tools/gn/rust_substitution_type.h | 29 + src/3rdparty/gn/tools/gn/rust_tool.cc | 160 + src/3rdparty/gn/tools/gn/rust_tool.h | 98 + src/3rdparty/gn/tools/gn/rust_values.cc | 9 + src/3rdparty/gn/tools/gn/rust_values.h | 67 + .../gn/tools/gn/rust_values_generator.cc | 206 + .../gn/tools/gn/rust_values_generator.h | 39 + src/3rdparty/gn/tools/gn/rust_variables.cc | 129 + src/3rdparty/gn/tools/gn/rust_variables.h | 38 + src/3rdparty/gn/tools/gn/scheduler.cc | 189 + src/3rdparty/gn/tools/gn/scheduler.h | 156 + src/3rdparty/gn/tools/gn/scope.cc | 593 ++ src/3rdparty/gn/tools/gn/scope.h | 397 + .../gn/tools/gn/scope_per_file_provider.cc | 116 + .../gn/tools/gn/scope_per_file_provider.h | 51 + .../gn/scope_per_file_provider_unittest.cc | 55 + src/3rdparty/gn/tools/gn/scope_unittest.cc | 335 + src/3rdparty/gn/tools/gn/settings.cc | 29 + src/3rdparty/gn/tools/gn/settings.h | 115 + src/3rdparty/gn/tools/gn/setup.cc | 862 ++ src/3rdparty/gn/tools/gn/setup.h | 189 + src/3rdparty/gn/tools/gn/setup_unittest.cc | 45 + src/3rdparty/gn/tools/gn/source_dir.cc | 150 + src/3rdparty/gn/tools/gn/source_dir.h | 153 + .../gn/tools/gn/source_dir_unittest.cc | 208 + src/3rdparty/gn/tools/gn/source_file.cc | 122 + src/3rdparty/gn/tools/gn/source_file.h | 141 + .../gn/tools/gn/source_file_unittest.cc | 20 + src/3rdparty/gn/tools/gn/standard_out.cc | 342 + src/3rdparty/gn/tools/gn/standard_out.h | 61 + src/3rdparty/gn/tools/gn/string_utils.cc | 347 + src/3rdparty/gn/tools/gn/string_utils.h | 53 + .../gn/tools/gn/string_utils_unittest.cc | 159 + src/3rdparty/gn/tools/gn/substitution_list.cc | 69 + src/3rdparty/gn/tools/gn/substitution_list.h | 46 + .../gn/tools/gn/substitution_pattern.cc | 144 + .../gn/tools/gn/substitution_pattern.h | 79 + .../tools/gn/substitution_pattern_unittest.cc | 73 + src/3rdparty/gn/tools/gn/substitution_type.cc | 193 + src/3rdparty/gn/tools/gn/substitution_type.h | 117 + .../gn/tools/gn/substitution_writer.cc | 598 ++ .../gn/tools/gn/substitution_writer.h | 238 + .../tools/gn/substitution_writer_unittest.cc | 321 + src/3rdparty/gn/tools/gn/switches.cc | 310 + src/3rdparty/gn/tools/gn/switches.h | 118 + src/3rdparty/gn/tools/gn/target.cc | 1003 +++ src/3rdparty/gn/tools/gn/target.h | 470 ++ src/3rdparty/gn/tools/gn/target_generator.cc | 458 + src/3rdparty/gn/tools/gn/target_generator.h | 87 + src/3rdparty/gn/tools/gn/target_unittest.cc | 1356 +++ src/3rdparty/gn/tools/gn/template.cc | 123 + src/3rdparty/gn/tools/gn/template.h | 67 + src/3rdparty/gn/tools/gn/template_unittest.cc | 93 + .../gn/tools/gn/test_with_scheduler.cc | 8 + .../gn/tools/gn/test_with_scheduler.h | 27 + src/3rdparty/gn/tools/gn/test_with_scope.cc | 253 + src/3rdparty/gn/tools/gn/test_with_scope.h | 123 + src/3rdparty/gn/tools/gn/token.cc | 22 + src/3rdparty/gn/tools/gn/token.h | 85 + src/3rdparty/gn/tools/gn/tokenizer.cc | 402 + src/3rdparty/gn/tools/gn/tokenizer.h | 90 + .../gn/tools/gn/tokenizer_unittest.cc | 205 + src/3rdparty/gn/tools/gn/tool.cc | 344 + src/3rdparty/gn/tools/gn/tool.h | 281 + src/3rdparty/gn/tools/gn/toolchain.cc | 140 + src/3rdparty/gn/tools/gn/toolchain.h | 129 + src/3rdparty/gn/tools/gn/trace.cc | 334 + src/3rdparty/gn/tools/gn/trace.h | 104 + src/3rdparty/gn/tools/gn/unique_vector.h | 163 + .../gn/tools/gn/unique_vector_unittest.cc | 45 + src/3rdparty/gn/tools/gn/value.cc | 272 + src/3rdparty/gn/tools/gn/value.h | 137 + src/3rdparty/gn/tools/gn/value_extractors.cc | 249 + src/3rdparty/gn/tools/gn/value_extractors.h | 89 + src/3rdparty/gn/tools/gn/value_unittest.cc | 60 + src/3rdparty/gn/tools/gn/variables.cc | 2273 +++++ src/3rdparty/gn/tools/gn/variables.h | 364 + src/3rdparty/gn/tools/gn/visibility.cc | 116 + src/3rdparty/gn/tools/gn/visibility.h | 68 + .../gn/tools/gn/visibility_unittest.cc | 53 + .../gn/tools/gn/visual_studio_utils.cc | 127 + .../gn/tools/gn/visual_studio_utils.h | 50 + .../tools/gn/visual_studio_utils_unittest.cc | 102 + .../gn/tools/gn/visual_studio_writer.cc | 913 ++ .../gn/tools/gn/visual_studio_writer.h | 168 + .../tools/gn/visual_studio_writer_unittest.cc | 200 + src/3rdparty/gn/tools/gn/xcode_object.cc | 990 +++ src/3rdparty/gn/tools/gn/xcode_object.h | 465 + .../gn/tools/gn/xcode_object_unittest.cc | 435 + src/3rdparty/gn/tools/gn/xcode_writer.cc | 664 ++ src/3rdparty/gn/tools/gn/xcode_writer.h | 89 + .../gn/tools/gn/xml_element_writer.cc | 114 + src/3rdparty/gn/tools/gn/xml_element_writer.h | 124 + .../tools/gn/xml_element_writer_unittest.cc | 93 + src/3rdparty/gn/util/auto_reset_event.h | 53 + src/3rdparty/gn/util/build_config.h | 196 + src/3rdparty/gn/util/exe_path.cc | 77 + src/3rdparty/gn/util/exe_path.h | 12 + src/3rdparty/gn/util/msg_loop.cc | 76 + src/3rdparty/gn/util/msg_loop.h | 47 + src/3rdparty/gn/util/semaphore.cc | 95 + src/3rdparty/gn/util/semaphore.h | 53 + src/3rdparty/gn/util/sys_info.cc | 79 + src/3rdparty/gn/util/sys_info.h | 13 + src/3rdparty/gn/util/task.h | 13 + src/3rdparty/gn/util/test/gn_test.cc | 162 + src/3rdparty/gn/util/test/test.h | 195 + src/3rdparty/gn/util/ticks.cc | 93 + src/3rdparty/gn/util/ticks.h | 45 + src/3rdparty/gn/util/worker_pool.cc | 150 + src/3rdparty/gn/util/worker_pool.h | 37 + 717 files changed, 124755 insertions(+) create mode 100644 src/3rdparty/gn/.clang-format create mode 100644 src/3rdparty/gn/.editorconfig create mode 100644 src/3rdparty/gn/.style.yapf create mode 100644 src/3rdparty/gn/AUTHORS create mode 100644 src/3rdparty/gn/LICENSE create mode 100644 src/3rdparty/gn/OWNERS create mode 100644 src/3rdparty/gn/README.md create mode 100644 src/3rdparty/gn/base/atomic_ref_count.h create mode 100644 src/3rdparty/gn/base/bind.h create mode 100644 src/3rdparty/gn/base/bind_internal.h create mode 100644 src/3rdparty/gn/base/callback.h create mode 100644 src/3rdparty/gn/base/callback_forward.h create mode 100644 src/3rdparty/gn/base/callback_internal.cc create mode 100644 src/3rdparty/gn/base/callback_internal.h create mode 100644 src/3rdparty/gn/base/command_line.cc create mode 100644 src/3rdparty/gn/base/command_line.h create mode 100644 src/3rdparty/gn/base/compiler_specific.h create mode 100644 src/3rdparty/gn/base/containers/circular_deque.h create mode 100644 src/3rdparty/gn/base/containers/flat_map.h create mode 100644 src/3rdparty/gn/base/containers/flat_set.h create mode 100644 src/3rdparty/gn/base/containers/flat_tree.h create mode 100644 src/3rdparty/gn/base/containers/queue.h create mode 100644 src/3rdparty/gn/base/containers/span.h create mode 100644 src/3rdparty/gn/base/containers/stack.h create mode 100644 src/3rdparty/gn/base/containers/vector_buffer.h create mode 100644 src/3rdparty/gn/base/environment.cc create mode 100644 src/3rdparty/gn/base/environment.h create mode 100644 src/3rdparty/gn/base/files/file.cc create mode 100644 src/3rdparty/gn/base/files/file.h create mode 100644 src/3rdparty/gn/base/files/file_enumerator.cc create mode 100644 src/3rdparty/gn/base/files/file_enumerator.h create mode 100644 src/3rdparty/gn/base/files/file_enumerator_posix.cc create mode 100644 src/3rdparty/gn/base/files/file_enumerator_win.cc create mode 100644 src/3rdparty/gn/base/files/file_path.cc create mode 100644 src/3rdparty/gn/base/files/file_path.h create mode 100644 src/3rdparty/gn/base/files/file_path_constants.cc create mode 100644 src/3rdparty/gn/base/files/file_posix.cc create mode 100644 src/3rdparty/gn/base/files/file_util.cc create mode 100644 src/3rdparty/gn/base/files/file_util.h create mode 100644 src/3rdparty/gn/base/files/file_util_linux.cc create mode 100644 src/3rdparty/gn/base/files/file_util_posix.cc create mode 100644 src/3rdparty/gn/base/files/file_util_win.cc create mode 100644 src/3rdparty/gn/base/files/file_win.cc create mode 100644 src/3rdparty/gn/base/files/platform_file.h create mode 100644 src/3rdparty/gn/base/files/scoped_file.cc create mode 100644 src/3rdparty/gn/base/files/scoped_file.h create mode 100644 src/3rdparty/gn/base/files/scoped_temp_dir.cc create mode 100644 src/3rdparty/gn/base/files/scoped_temp_dir.h create mode 100644 src/3rdparty/gn/base/gtest_prod_util.h create mode 100644 src/3rdparty/gn/base/json/json_parser.cc create mode 100644 src/3rdparty/gn/base/json/json_parser.h create mode 100644 src/3rdparty/gn/base/json/json_reader.cc create mode 100644 src/3rdparty/gn/base/json/json_reader.h create mode 100644 src/3rdparty/gn/base/json/json_value_converter.cc create mode 100644 src/3rdparty/gn/base/json/json_value_converter.h create mode 100644 src/3rdparty/gn/base/json/json_writer.cc create mode 100644 src/3rdparty/gn/base/json/json_writer.h create mode 100644 src/3rdparty/gn/base/json/string_escape.cc create mode 100644 src/3rdparty/gn/base/json/string_escape.h create mode 100644 src/3rdparty/gn/base/logging.cc create mode 100644 src/3rdparty/gn/base/logging.h create mode 100644 src/3rdparty/gn/base/mac/bundle_locations.h create mode 100644 src/3rdparty/gn/base/mac/mac_logging.h create mode 100644 src/3rdparty/gn/base/mac/mac_logging.mm create mode 100644 src/3rdparty/gn/base/mac/scoped_cftyperef.h create mode 100644 src/3rdparty/gn/base/mac/scoped_typeref.h create mode 100644 src/3rdparty/gn/base/macros.h create mode 100644 src/3rdparty/gn/base/md5.cc create mode 100644 src/3rdparty/gn/base/md5.h create mode 100644 src/3rdparty/gn/base/memory/free_deleter.h create mode 100644 src/3rdparty/gn/base/memory/ptr_util.h create mode 100644 src/3rdparty/gn/base/memory/raw_scoped_refptr_mismatch_checker.h create mode 100644 src/3rdparty/gn/base/memory/ref_counted.cc create mode 100644 src/3rdparty/gn/base/memory/ref_counted.h create mode 100644 src/3rdparty/gn/base/memory/scoped_policy.h create mode 100644 src/3rdparty/gn/base/memory/scoped_refptr.h create mode 100644 src/3rdparty/gn/base/memory/weak_ptr.cc create mode 100644 src/3rdparty/gn/base/memory/weak_ptr.h create mode 100644 src/3rdparty/gn/base/numerics/checked_math.h create mode 100644 src/3rdparty/gn/base/numerics/checked_math_impl.h create mode 100644 src/3rdparty/gn/base/numerics/clamped_math.h create mode 100644 src/3rdparty/gn/base/numerics/clamped_math_impl.h create mode 100644 src/3rdparty/gn/base/numerics/math_constants.h create mode 100644 src/3rdparty/gn/base/numerics/ranges.h create mode 100644 src/3rdparty/gn/base/numerics/safe_conversions.h create mode 100644 src/3rdparty/gn/base/numerics/safe_conversions_arm_impl.h create mode 100644 src/3rdparty/gn/base/numerics/safe_conversions_impl.h create mode 100644 src/3rdparty/gn/base/numerics/safe_math.h create mode 100644 src/3rdparty/gn/base/numerics/safe_math_arm_impl.h create mode 100644 src/3rdparty/gn/base/numerics/safe_math_clang_gcc_impl.h create mode 100644 src/3rdparty/gn/base/numerics/safe_math_shared_impl.h create mode 100644 src/3rdparty/gn/base/optional.h create mode 100644 src/3rdparty/gn/base/posix/eintr_wrapper.h create mode 100644 src/3rdparty/gn/base/posix/file_descriptor_shuffle.cc create mode 100644 src/3rdparty/gn/base/posix/file_descriptor_shuffle.h create mode 100644 src/3rdparty/gn/base/posix/safe_strerror.cc create mode 100644 src/3rdparty/gn/base/posix/safe_strerror.h create mode 100644 src/3rdparty/gn/base/scoped_clear_errno.h create mode 100644 src/3rdparty/gn/base/scoped_generic.h create mode 100644 src/3rdparty/gn/base/sha1.cc create mode 100644 src/3rdparty/gn/base/sha1.h create mode 100644 src/3rdparty/gn/base/stl_util.h create mode 100644 src/3rdparty/gn/base/strings/char_traits.h create mode 100644 src/3rdparty/gn/base/strings/string16.cc create mode 100644 src/3rdparty/gn/base/strings/string16.h create mode 100644 src/3rdparty/gn/base/strings/string_number_conversions.cc create mode 100644 src/3rdparty/gn/base/strings/string_number_conversions.h create mode 100644 src/3rdparty/gn/base/strings/string_piece.cc create mode 100644 src/3rdparty/gn/base/strings/string_piece.h create mode 100644 src/3rdparty/gn/base/strings/string_piece_forward.h create mode 100644 src/3rdparty/gn/base/strings/string_split.cc create mode 100644 src/3rdparty/gn/base/strings/string_split.h create mode 100644 src/3rdparty/gn/base/strings/string_tokenizer.h create mode 100644 src/3rdparty/gn/base/strings/string_util.cc create mode 100644 src/3rdparty/gn/base/strings/string_util.h create mode 100644 src/3rdparty/gn/base/strings/string_util_constants.cc create mode 100644 src/3rdparty/gn/base/strings/string_util_posix.h create mode 100644 src/3rdparty/gn/base/strings/string_util_win.h create mode 100644 src/3rdparty/gn/base/strings/stringize_macros.h create mode 100644 src/3rdparty/gn/base/strings/stringprintf.cc create mode 100644 src/3rdparty/gn/base/strings/stringprintf.h create mode 100644 src/3rdparty/gn/base/strings/utf_offset_string_conversions.cc create mode 100644 src/3rdparty/gn/base/strings/utf_offset_string_conversions.h create mode 100644 src/3rdparty/gn/base/strings/utf_string_conversion_utils.cc create mode 100644 src/3rdparty/gn/base/strings/utf_string_conversion_utils.h create mode 100644 src/3rdparty/gn/base/strings/utf_string_conversions.cc create mode 100644 src/3rdparty/gn/base/strings/utf_string_conversions.h create mode 100644 src/3rdparty/gn/base/sys_byteorder.h create mode 100644 src/3rdparty/gn/base/template_util.h create mode 100644 src/3rdparty/gn/base/third_party/icu/LICENSE create mode 100644 src/3rdparty/gn/base/third_party/icu/README.chromium create mode 100644 src/3rdparty/gn/base/third_party/icu/icu_utf.cc create mode 100644 src/3rdparty/gn/base/third_party/icu/icu_utf.h create mode 100644 src/3rdparty/gn/base/timer/elapsed_timer.cc create mode 100644 src/3rdparty/gn/base/timer/elapsed_timer.h create mode 100644 src/3rdparty/gn/base/value_iterators.cc create mode 100644 src/3rdparty/gn/base/value_iterators.h create mode 100644 src/3rdparty/gn/base/values.cc create mode 100644 src/3rdparty/gn/base/values.h create mode 100644 src/3rdparty/gn/base/win/registry.cc create mode 100644 src/3rdparty/gn/base/win/registry.h create mode 100644 src/3rdparty/gn/base/win/scoped_handle.cc create mode 100644 src/3rdparty/gn/base/win/scoped_handle.h create mode 100644 src/3rdparty/gn/base/win/scoped_process_information.cc create mode 100644 src/3rdparty/gn/base/win/scoped_process_information.h create mode 100644 src/3rdparty/gn/base/win/windows_types.h create mode 100644 src/3rdparty/gn/build/build_aix.ninja.template create mode 100644 src/3rdparty/gn/build/build_linux.ninja.template create mode 100644 src/3rdparty/gn/build/build_mac.ninja.template create mode 100644 src/3rdparty/gn/build/build_openbsd.ninja.template create mode 100644 src/3rdparty/gn/build/build_win.ninja.template create mode 100755 src/3rdparty/gn/build/full_test.py create mode 100755 src/3rdparty/gn/build/gen.py create mode 100644 src/3rdparty/gn/docs/cross_compiles.md create mode 100644 src/3rdparty/gn/docs/faq.md create mode 100644 src/3rdparty/gn/docs/language.md create mode 100644 src/3rdparty/gn/docs/quick_start.md create mode 100644 src/3rdparty/gn/docs/reference.md create mode 100644 src/3rdparty/gn/docs/standalone.md create mode 100644 src/3rdparty/gn/docs/style_guide.md create mode 100644 src/3rdparty/gn/examples/rust_example/.gn create mode 100644 src/3rdparty/gn/examples/rust_example/BUILD.gn create mode 100644 src/3rdparty/gn/examples/rust_example/BUILDCONFIG.gn create mode 100644 src/3rdparty/gn/examples/rust_example/README.txt create mode 100644 src/3rdparty/gn/examples/rust_example/build/BUILD.gn create mode 100644 src/3rdparty/gn/examples/rust_example/hello_world/bar/src/BUILD.gn create mode 100644 src/3rdparty/gn/examples/rust_example/hello_world/bar/src/lib.rs create mode 100644 src/3rdparty/gn/examples/rust_example/hello_world/foo/src/BUILD.gn create mode 100644 src/3rdparty/gn/examples/rust_example/hello_world/foo/src/lib.rs create mode 100644 src/3rdparty/gn/examples/rust_example/hello_world/src/BUILD.gn create mode 100644 src/3rdparty/gn/examples/rust_example/hello_world/src/main.rs create mode 100644 src/3rdparty/gn/examples/simple_build/.gn create mode 100644 src/3rdparty/gn/examples/simple_build/BUILD.gn create mode 100644 src/3rdparty/gn/examples/simple_build/README.md create mode 100644 src/3rdparty/gn/examples/simple_build/build/BUILD.gn create mode 100644 src/3rdparty/gn/examples/simple_build/build/BUILDCONFIG.gn create mode 100644 src/3rdparty/gn/examples/simple_build/build/toolchain/BUILD.gn create mode 100644 src/3rdparty/gn/examples/simple_build/hello.cc create mode 100644 src/3rdparty/gn/examples/simple_build/hello_shared.cc create mode 100644 src/3rdparty/gn/examples/simple_build/hello_shared.h create mode 100644 src/3rdparty/gn/examples/simple_build/hello_static.cc create mode 100644 src/3rdparty/gn/examples/simple_build/hello_static.h create mode 100644 src/3rdparty/gn/infra/README.recipes.md create mode 100644 src/3rdparty/gn/infra/config/recipes.cfg create mode 100644 src/3rdparty/gn/infra/config/refs.cfg create mode 100644 src/3rdparty/gn/infra/recipe_modules/macos_sdk/__init__.py create mode 100644 src/3rdparty/gn/infra/recipe_modules/macos_sdk/api.py create mode 100644 src/3rdparty/gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json create mode 100644 src/3rdparty/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json create mode 100644 src/3rdparty/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json create mode 100644 src/3rdparty/gn/infra/recipe_modules/macos_sdk/examples/full.py create mode 100644 src/3rdparty/gn/infra/recipe_modules/windows_sdk/__init__.py create mode 100644 src/3rdparty/gn/infra/recipe_modules/windows_sdk/api.py create mode 100644 src/3rdparty/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json create mode 100644 src/3rdparty/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json create mode 100644 src/3rdparty/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json create mode 100644 src/3rdparty/gn/infra/recipe_modules/windows_sdk/examples/full.py create mode 100755 src/3rdparty/gn/infra/recipes.py create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/ci_linux.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/ci_mac.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/ci_win.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/cipd_exists.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/cipd_register.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/cq_linux.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/cq_mac.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.expected/cq_win.json create mode 100644 src/3rdparty/gn/infra/recipes/gn.py create mode 100644 src/3rdparty/gn/src/gn/function_filter_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/action_target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/action_target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/action_target_generator_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/action_values.cc create mode 100644 src/3rdparty/gn/tools/gn/action_values.h create mode 100644 src/3rdparty/gn/tools/gn/analyzer.cc create mode 100644 src/3rdparty/gn/tools/gn/analyzer.h create mode 100644 src/3rdparty/gn/tools/gn/analyzer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/args.cc create mode 100644 src/3rdparty/gn/tools/gn/args.h create mode 100644 src/3rdparty/gn/tools/gn/args_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/binary_target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/binary_target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/build_settings.cc create mode 100644 src/3rdparty/gn/tools/gn/build_settings.h create mode 100644 src/3rdparty/gn/tools/gn/builder.cc create mode 100644 src/3rdparty/gn/tools/gn/builder.h create mode 100644 src/3rdparty/gn/tools/gn/builder_record.cc create mode 100644 src/3rdparty/gn/tools/gn/builder_record.h create mode 100644 src/3rdparty/gn/tools/gn/builder_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/bundle_data.cc create mode 100644 src/3rdparty/gn/tools/gn/bundle_data.h create mode 100644 src/3rdparty/gn/tools/gn/bundle_data_target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/bundle_data_target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/bundle_file_rule.cc create mode 100644 src/3rdparty/gn/tools/gn/bundle_file_rule.h create mode 100644 src/3rdparty/gn/tools/gn/c_include_iterator.cc create mode 100644 src/3rdparty/gn/tools/gn/c_include_iterator.h create mode 100644 src/3rdparty/gn/tools/gn/c_include_iterator_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/c_substitution_type.cc create mode 100644 src/3rdparty/gn/tools/gn/c_substitution_type.h create mode 100644 src/3rdparty/gn/tools/gn/c_tool.cc create mode 100644 src/3rdparty/gn/tools/gn/c_tool.h create mode 100644 src/3rdparty/gn/tools/gn/command_analyze.cc create mode 100644 src/3rdparty/gn/tools/gn/command_args.cc create mode 100644 src/3rdparty/gn/tools/gn/command_check.cc create mode 100644 src/3rdparty/gn/tools/gn/command_clean.cc create mode 100644 src/3rdparty/gn/tools/gn/command_desc.cc create mode 100644 src/3rdparty/gn/tools/gn/command_format.cc create mode 100644 src/3rdparty/gn/tools/gn/command_format.h create mode 100644 src/3rdparty/gn/tools/gn/command_format_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/command_gen.cc create mode 100644 src/3rdparty/gn/tools/gn/command_help.cc create mode 100644 src/3rdparty/gn/tools/gn/command_ls.cc create mode 100644 src/3rdparty/gn/tools/gn/command_meta.cc create mode 100644 src/3rdparty/gn/tools/gn/command_path.cc create mode 100644 src/3rdparty/gn/tools/gn/command_refs.cc create mode 100644 src/3rdparty/gn/tools/gn/commands.cc create mode 100644 src/3rdparty/gn/tools/gn/commands.h create mode 100644 src/3rdparty/gn/tools/gn/compile_commands_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/compile_commands_writer.h create mode 100644 src/3rdparty/gn/tools/gn/compile_commands_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/config.cc create mode 100644 src/3rdparty/gn/tools/gn/config.h create mode 100644 src/3rdparty/gn/tools/gn/config_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/config_values.cc create mode 100644 src/3rdparty/gn/tools/gn/config_values.h create mode 100644 src/3rdparty/gn/tools/gn/config_values_extractors.cc create mode 100644 src/3rdparty/gn/tools/gn/config_values_extractors.h create mode 100644 src/3rdparty/gn/tools/gn/config_values_extractors_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/config_values_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/config_values_generator.h create mode 100644 src/3rdparty/gn/tools/gn/copy_target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/copy_target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/create_bundle_target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/create_bundle_target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/deps_iterator.cc create mode 100644 src/3rdparty/gn/tools/gn/deps_iterator.h create mode 100644 src/3rdparty/gn/tools/gn/desc_builder.cc create mode 100644 src/3rdparty/gn/tools/gn/desc_builder.h create mode 100644 src/3rdparty/gn/tools/gn/eclipse_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/eclipse_writer.h create mode 100644 src/3rdparty/gn/tools/gn/err.cc create mode 100644 src/3rdparty/gn/tools/gn/err.h create mode 100644 src/3rdparty/gn/tools/gn/escape.cc create mode 100644 src/3rdparty/gn/tools/gn/escape.h create mode 100644 src/3rdparty/gn/tools/gn/escape_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/exec_process.cc create mode 100644 src/3rdparty/gn/tools/gn/exec_process.h create mode 100644 src/3rdparty/gn/tools/gn/exec_process_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/filesystem_utils.cc create mode 100644 src/3rdparty/gn/tools/gn/filesystem_utils.h create mode 100644 src/3rdparty/gn/tools/gn/filesystem_utils_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/001.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/001.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/002.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/002.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/003.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/003.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/004.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/004.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/005.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/005.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/006.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/006.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/007.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/007.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/008.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/008.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/009.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/009.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/010.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/010.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/011.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/011.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/012.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/012.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/013.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/013.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/014.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/014.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/015.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/015.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/016.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/016.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/017.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/017.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/018.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/018.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/019.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/019.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/020.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/020.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/021.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/021.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/022.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/022.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/023.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/023.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/024.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/024.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/025.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/025.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/026.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/026.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/027.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/027.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/028.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/028.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/029.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/029.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/030.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/030.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/031.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/031.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/032.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/032.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/033.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/033.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/034.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/035.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/035.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/036.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/036.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/037.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/037.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/038.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/038.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/039.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/039.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/040.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/041.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/041.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/042.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/042.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/043.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/043.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/044.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/044.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/045.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/045.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/046.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/046.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/047.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/047.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/048.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/048.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/049.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/050.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/050.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/051.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/051.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/052.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/052.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/053.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/053.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/054.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/054.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/055.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/055.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/056.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/056.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/057.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/057.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/058.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/058.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/059.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/059.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/060.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/060.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/061.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/061.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/062.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/062.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/063.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/063.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/064.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/064.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/065.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/065.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/066.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/066.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/067.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/067.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/068.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/068.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/069.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/069.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/070.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/070.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/071.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/071.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/072.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/072.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/073.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/073.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/074.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/074.golden create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/075.gn create mode 100644 src/3rdparty/gn/tools/gn/format_test_data/075.golden create mode 100644 src/3rdparty/gn/tools/gn/frameworks_utils.cc create mode 100644 src/3rdparty/gn/tools/gn/frameworks_utils.h create mode 100644 src/3rdparty/gn/tools/gn/frameworks_utils_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_exec_script.cc create mode 100644 src/3rdparty/gn/tools/gn/function_filter.cc create mode 100644 src/3rdparty/gn/tools/gn/function_foreach.cc create mode 100644 src/3rdparty/gn/tools/gn/function_foreach_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_forward_variables_from.cc create mode 100644 src/3rdparty/gn/tools/gn/function_forward_variables_from_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_get_label_info.cc create mode 100644 src/3rdparty/gn/tools/gn/function_get_label_info_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_get_path_info.cc create mode 100644 src/3rdparty/gn/tools/gn/function_get_path_info_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_get_target_outputs.cc create mode 100644 src/3rdparty/gn/tools/gn/function_get_target_outputs_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_process_file_template.cc create mode 100644 src/3rdparty/gn/tools/gn/function_process_file_template_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_read_file.cc create mode 100644 src/3rdparty/gn/tools/gn/function_rebase_path.cc create mode 100644 src/3rdparty/gn/tools/gn/function_rebase_path_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_set_default_toolchain.cc create mode 100644 src/3rdparty/gn/tools/gn/function_set_defaults.cc create mode 100644 src/3rdparty/gn/tools/gn/function_template.cc create mode 100644 src/3rdparty/gn/tools/gn/function_template_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_toolchain.cc create mode 100644 src/3rdparty/gn/tools/gn/function_toolchain_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/function_write_file.cc create mode 100644 src/3rdparty/gn/tools/gn/function_write_file_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/functions.cc create mode 100644 src/3rdparty/gn/tools/gn/functions.h create mode 100644 src/3rdparty/gn/tools/gn/functions_target.cc create mode 100644 src/3rdparty/gn/tools/gn/functions_target_rust_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/functions_target_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/functions_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/general_tool.cc create mode 100644 src/3rdparty/gn/tools/gn/general_tool.h create mode 100644 src/3rdparty/gn/tools/gn/generated_file_target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/generated_file_target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/gn_main.cc create mode 100644 src/3rdparty/gn/tools/gn/group_target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/group_target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/header_checker.cc create mode 100644 src/3rdparty/gn/tools/gn/header_checker.h create mode 100644 src/3rdparty/gn/tools/gn/header_checker_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/import_manager.cc create mode 100644 src/3rdparty/gn/tools/gn/import_manager.h create mode 100644 src/3rdparty/gn/tools/gn/inherited_libraries.cc create mode 100644 src/3rdparty/gn/tools/gn/inherited_libraries.h create mode 100644 src/3rdparty/gn/tools/gn/inherited_libraries_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/input_conversion.cc create mode 100644 src/3rdparty/gn/tools/gn/input_conversion.h create mode 100644 src/3rdparty/gn/tools/gn/input_conversion_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/input_file.cc create mode 100644 src/3rdparty/gn/tools/gn/input_file.h create mode 100644 src/3rdparty/gn/tools/gn/input_file_manager.cc create mode 100644 src/3rdparty/gn/tools/gn/input_file_manager.h create mode 100644 src/3rdparty/gn/tools/gn/item.cc create mode 100644 src/3rdparty/gn/tools/gn/item.h create mode 100644 src/3rdparty/gn/tools/gn/json_project_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/json_project_writer.h create mode 100644 src/3rdparty/gn/tools/gn/json_project_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/label.cc create mode 100644 src/3rdparty/gn/tools/gn/label.h create mode 100644 src/3rdparty/gn/tools/gn/label_pattern.cc create mode 100644 src/3rdparty/gn/tools/gn/label_pattern.h create mode 100644 src/3rdparty/gn/tools/gn/label_pattern_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/label_ptr.h create mode 100644 src/3rdparty/gn/tools/gn/label_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/lib_file.cc create mode 100644 src/3rdparty/gn/tools/gn/lib_file.h create mode 100644 src/3rdparty/gn/tools/gn/loader.cc create mode 100644 src/3rdparty/gn/tools/gn/loader.h create mode 100644 src/3rdparty/gn/tools/gn/loader_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/location.cc create mode 100644 src/3rdparty/gn/tools/gn/location.h create mode 100644 src/3rdparty/gn/tools/gn/metadata.cc create mode 100644 src/3rdparty/gn/tools/gn/metadata.h create mode 100644 src/3rdparty/gn/tools/gn/metadata_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/metadata_walk.cc create mode 100644 src/3rdparty/gn/tools/gn/metadata_walk.h create mode 100644 src/3rdparty/gn/tools/gn/metadata_walk_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/misc/emacs/gn-mode.el create mode 100755 src/3rdparty/gn/tools/gn/misc/help_as_html.py create mode 100644 src/3rdparty/gn/tools/gn/misc/tm/GN.tmLanguage create mode 100644 src/3rdparty/gn/tools/gn/misc/tm/GN.tmPreferences create mode 100644 src/3rdparty/gn/tools/gn/misc/vim/README.md create mode 100644 src/3rdparty/gn/tools/gn/misc/vim/autoload/gn.vim create mode 100644 src/3rdparty/gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim create mode 100644 src/3rdparty/gn/tools/gn/misc/vim/ftplugin/gn.vim create mode 100644 src/3rdparty/gn/tools/gn/misc/vim/gn-format.py create mode 100644 src/3rdparty/gn/tools/gn/misc/vim/syntax/gn.vim create mode 100644 src/3rdparty/gn/tools/gn/ninja_action_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_action_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_action_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_binary_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_binary_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_binary_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_build_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_build_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_build_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_bundle_data_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_bundle_data_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_c_binary_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_c_binary_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_c_binary_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_copy_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_copy_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_copy_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_create_bundle_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_create_bundle_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_generated_file_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_generated_file_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_generated_file_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_group_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_group_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_group_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_rust_binary_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_rust_binary_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_target_command_util.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_target_command_util.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_target_command_util_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_target_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_target_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_target_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_toolchain_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_toolchain_writer.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_toolchain_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_utils.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_utils.h create mode 100644 src/3rdparty/gn/tools/gn/ninja_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/ninja_writer.h create mode 100644 src/3rdparty/gn/tools/gn/operators.cc create mode 100644 src/3rdparty/gn/tools/gn/operators.h create mode 100644 src/3rdparty/gn/tools/gn/operators_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/ordered_set.h create mode 100644 src/3rdparty/gn/tools/gn/output_conversion.cc create mode 100644 src/3rdparty/gn/tools/gn/output_conversion.h create mode 100644 src/3rdparty/gn/tools/gn/output_conversion_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/output_file.cc create mode 100644 src/3rdparty/gn/tools/gn/output_file.h create mode 100644 src/3rdparty/gn/tools/gn/parse_node_value_adapter.cc create mode 100644 src/3rdparty/gn/tools/gn/parse_node_value_adapter.h create mode 100644 src/3rdparty/gn/tools/gn/parse_tree.cc create mode 100644 src/3rdparty/gn/tools/gn/parse_tree.h create mode 100644 src/3rdparty/gn/tools/gn/parse_tree_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/parser.cc create mode 100644 src/3rdparty/gn/tools/gn/parser.h create mode 100644 src/3rdparty/gn/tools/gn/parser_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/path_output.cc create mode 100644 src/3rdparty/gn/tools/gn/path_output.h create mode 100644 src/3rdparty/gn/tools/gn/path_output_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/pattern.cc create mode 100644 src/3rdparty/gn/tools/gn/pattern.h create mode 100644 src/3rdparty/gn/tools/gn/pattern_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/pool.cc create mode 100644 src/3rdparty/gn/tools/gn/pool.h create mode 100644 src/3rdparty/gn/tools/gn/qmake_link_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/qmake_link_writer.h create mode 100644 src/3rdparty/gn/tools/gn/qmake_link_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/qt_creator_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/qt_creator_writer.h create mode 100644 src/3rdparty/gn/tools/gn/runtime_deps.cc create mode 100644 src/3rdparty/gn/tools/gn/runtime_deps.h create mode 100644 src/3rdparty/gn/tools/gn/runtime_deps_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/rust_substitution_type.cc create mode 100644 src/3rdparty/gn/tools/gn/rust_substitution_type.h create mode 100644 src/3rdparty/gn/tools/gn/rust_tool.cc create mode 100644 src/3rdparty/gn/tools/gn/rust_tool.h create mode 100644 src/3rdparty/gn/tools/gn/rust_values.cc create mode 100644 src/3rdparty/gn/tools/gn/rust_values.h create mode 100644 src/3rdparty/gn/tools/gn/rust_values_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/rust_values_generator.h create mode 100644 src/3rdparty/gn/tools/gn/rust_variables.cc create mode 100644 src/3rdparty/gn/tools/gn/rust_variables.h create mode 100644 src/3rdparty/gn/tools/gn/scheduler.cc create mode 100644 src/3rdparty/gn/tools/gn/scheduler.h create mode 100644 src/3rdparty/gn/tools/gn/scope.cc create mode 100644 src/3rdparty/gn/tools/gn/scope.h create mode 100644 src/3rdparty/gn/tools/gn/scope_per_file_provider.cc create mode 100644 src/3rdparty/gn/tools/gn/scope_per_file_provider.h create mode 100644 src/3rdparty/gn/tools/gn/scope_per_file_provider_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/scope_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/settings.cc create mode 100644 src/3rdparty/gn/tools/gn/settings.h create mode 100644 src/3rdparty/gn/tools/gn/setup.cc create mode 100644 src/3rdparty/gn/tools/gn/setup.h create mode 100644 src/3rdparty/gn/tools/gn/setup_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/source_dir.cc create mode 100644 src/3rdparty/gn/tools/gn/source_dir.h create mode 100644 src/3rdparty/gn/tools/gn/source_dir_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/source_file.cc create mode 100644 src/3rdparty/gn/tools/gn/source_file.h create mode 100644 src/3rdparty/gn/tools/gn/source_file_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/standard_out.cc create mode 100644 src/3rdparty/gn/tools/gn/standard_out.h create mode 100644 src/3rdparty/gn/tools/gn/string_utils.cc create mode 100644 src/3rdparty/gn/tools/gn/string_utils.h create mode 100644 src/3rdparty/gn/tools/gn/string_utils_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/substitution_list.cc create mode 100644 src/3rdparty/gn/tools/gn/substitution_list.h create mode 100644 src/3rdparty/gn/tools/gn/substitution_pattern.cc create mode 100644 src/3rdparty/gn/tools/gn/substitution_pattern.h create mode 100644 src/3rdparty/gn/tools/gn/substitution_pattern_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/substitution_type.cc create mode 100644 src/3rdparty/gn/tools/gn/substitution_type.h create mode 100644 src/3rdparty/gn/tools/gn/substitution_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/substitution_writer.h create mode 100644 src/3rdparty/gn/tools/gn/substitution_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/switches.cc create mode 100644 src/3rdparty/gn/tools/gn/switches.h create mode 100644 src/3rdparty/gn/tools/gn/target.cc create mode 100644 src/3rdparty/gn/tools/gn/target.h create mode 100644 src/3rdparty/gn/tools/gn/target_generator.cc create mode 100644 src/3rdparty/gn/tools/gn/target_generator.h create mode 100644 src/3rdparty/gn/tools/gn/target_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/template.cc create mode 100644 src/3rdparty/gn/tools/gn/template.h create mode 100644 src/3rdparty/gn/tools/gn/template_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/test_with_scheduler.cc create mode 100644 src/3rdparty/gn/tools/gn/test_with_scheduler.h create mode 100644 src/3rdparty/gn/tools/gn/test_with_scope.cc create mode 100644 src/3rdparty/gn/tools/gn/test_with_scope.h create mode 100644 src/3rdparty/gn/tools/gn/token.cc create mode 100644 src/3rdparty/gn/tools/gn/token.h create mode 100644 src/3rdparty/gn/tools/gn/tokenizer.cc create mode 100644 src/3rdparty/gn/tools/gn/tokenizer.h create mode 100644 src/3rdparty/gn/tools/gn/tokenizer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/tool.cc create mode 100644 src/3rdparty/gn/tools/gn/tool.h create mode 100644 src/3rdparty/gn/tools/gn/toolchain.cc create mode 100644 src/3rdparty/gn/tools/gn/toolchain.h create mode 100644 src/3rdparty/gn/tools/gn/trace.cc create mode 100644 src/3rdparty/gn/tools/gn/trace.h create mode 100644 src/3rdparty/gn/tools/gn/unique_vector.h create mode 100644 src/3rdparty/gn/tools/gn/unique_vector_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/value.cc create mode 100644 src/3rdparty/gn/tools/gn/value.h create mode 100644 src/3rdparty/gn/tools/gn/value_extractors.cc create mode 100644 src/3rdparty/gn/tools/gn/value_extractors.h create mode 100644 src/3rdparty/gn/tools/gn/value_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/variables.cc create mode 100644 src/3rdparty/gn/tools/gn/variables.h create mode 100644 src/3rdparty/gn/tools/gn/visibility.cc create mode 100644 src/3rdparty/gn/tools/gn/visibility.h create mode 100644 src/3rdparty/gn/tools/gn/visibility_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/visual_studio_utils.cc create mode 100644 src/3rdparty/gn/tools/gn/visual_studio_utils.h create mode 100644 src/3rdparty/gn/tools/gn/visual_studio_utils_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/visual_studio_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/visual_studio_writer.h create mode 100644 src/3rdparty/gn/tools/gn/visual_studio_writer_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/xcode_object.cc create mode 100644 src/3rdparty/gn/tools/gn/xcode_object.h create mode 100644 src/3rdparty/gn/tools/gn/xcode_object_unittest.cc create mode 100644 src/3rdparty/gn/tools/gn/xcode_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/xcode_writer.h create mode 100644 src/3rdparty/gn/tools/gn/xml_element_writer.cc create mode 100644 src/3rdparty/gn/tools/gn/xml_element_writer.h create mode 100644 src/3rdparty/gn/tools/gn/xml_element_writer_unittest.cc create mode 100644 src/3rdparty/gn/util/auto_reset_event.h create mode 100644 src/3rdparty/gn/util/build_config.h create mode 100644 src/3rdparty/gn/util/exe_path.cc create mode 100644 src/3rdparty/gn/util/exe_path.h create mode 100644 src/3rdparty/gn/util/msg_loop.cc create mode 100644 src/3rdparty/gn/util/msg_loop.h create mode 100644 src/3rdparty/gn/util/semaphore.cc create mode 100644 src/3rdparty/gn/util/semaphore.h create mode 100644 src/3rdparty/gn/util/sys_info.cc create mode 100644 src/3rdparty/gn/util/sys_info.h create mode 100644 src/3rdparty/gn/util/task.h create mode 100644 src/3rdparty/gn/util/test/gn_test.cc create mode 100644 src/3rdparty/gn/util/test/test.h create mode 100644 src/3rdparty/gn/util/ticks.cc create mode 100644 src/3rdparty/gn/util/ticks.h create mode 100644 src/3rdparty/gn/util/worker_pool.cc create mode 100644 src/3rdparty/gn/util/worker_pool.h diff --git a/src/3rdparty/gn/.clang-format b/src/3rdparty/gn/.clang-format new file mode 100644 index 00000000000..b17a52a80ac --- /dev/null +++ b/src/3rdparty/gn/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Chromium +Standard: Cpp11 diff --git a/src/3rdparty/gn/.editorconfig b/src/3rdparty/gn/.editorconfig new file mode 100644 index 00000000000..2491fdfae55 --- /dev/null +++ b/src/3rdparty/gn/.editorconfig @@ -0,0 +1,3 @@ +[*.py] +indent_style = space +indent_size = 2 diff --git a/src/3rdparty/gn/.style.yapf b/src/3rdparty/gn/.style.yapf new file mode 100644 index 00000000000..de0c6a70f38 --- /dev/null +++ b/src/3rdparty/gn/.style.yapf @@ -0,0 +1,2 @@ +[style] +based_on_style = chromium diff --git a/src/3rdparty/gn/AUTHORS b/src/3rdparty/gn/AUTHORS new file mode 100644 index 00000000000..17b4959f9ce --- /dev/null +++ b/src/3rdparty/gn/AUTHORS @@ -0,0 +1,45 @@ +# Names should be added to this file with this pattern: +# +# For individuals: +# Name +# +# For organizations: +# Organization +# +# See python fnmatch module documentation for more information. + +Google Inc. <*@google.com> +IBM Inc. <*@*.ibm.com> +Loongson Technology Corporation Limited. <*@loongson.cn> +MIPS Technologies, Inc. <*@mips.com> +NVIDIA Corporation <*@nvidia.com> +Opera Software ASA <*@opera.com> +The Chromium Authors <*@chromium.org> +Vewd Software AS <*@vewd.com> +Vivaldi Technologies AS <*@vivaldi.com> +Yandex LLC <*@yandex-team.ru> + +Alexis Menard +Alfredo Mazzinghi +Andrew Boyarshin +Anuj Kumar Sharma +DanCraft99 +Gergely Nagy +Ilia K +Ivan Naydonov +Joe Armstrong +Julien Brianceau +Kal Conley +Kamil Rytarowski +Martijn Croonen +Matej Knopp +Michael Gilbert +Milko Leporis +Mohan Reddy +Raphael Kubo da Costa +Riku Voipio +Saikrishna Arcot +Tim Niederhausen +Tomas Popela +Tripta Gupta +Yuriy Taraday diff --git a/src/3rdparty/gn/LICENSE b/src/3rdparty/gn/LICENSE new file mode 100644 index 00000000000..a32e00ce6be --- /dev/null +++ b/src/3rdparty/gn/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/3rdparty/gn/OWNERS b/src/3rdparty/gn/OWNERS new file mode 100644 index 00000000000..d78697cf06f --- /dev/null +++ b/src/3rdparty/gn/OWNERS @@ -0,0 +1,3 @@ +brettw@chromium.org +phosek@chromium.org +scottmg@chromium.org diff --git a/src/3rdparty/gn/README.md b/src/3rdparty/gn/README.md new file mode 100644 index 00000000000..a8d9fa3724f --- /dev/null +++ b/src/3rdparty/gn/README.md @@ -0,0 +1,90 @@ +# GN + +GN is a meta-build system that generates build files for +[Ninja](https://ninja-build.org). + +Related resources: + + * Documentation in [docs/](https://gn.googlesource.com/gn/+/master/docs/). + * An introductory [presentation](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing). + * The [mailing list](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev). + +## Getting a binary + +You can download the latest version of GN binary for +[Linux](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest), +[macOS](https://chrome-infra-packages.appspot.com/dl/gn/gn/mac-amd64/+/latest) and +[Windows](https://chrome-infra-packages.appspot.com/dl/gn/gn/windows-amd64/+/latest). + +Alternatively, you can build GN from source: + + git clone https://gn.googlesource.com/gn + cd gn + python build/gen.py + ninja -C out + # To run tests: + out/gn_unittests + +On Windows, it is expected that `cl.exe`, `link.exe`, and `lib.exe` can be found +in `PATH`, so you'll want to run from a Visual Studio command prompt, or +similar. + +On Linux and Mac, the default compiler is `clang++`, a recent version is +expected to be found in `PATH`. This can be overridden by setting `CC`, `CXX`, +and `AR`. + +## Examples + +There is a simple example in [examples/simple_build](examples/simple_build) +directory that is a good place to get started with the minimal configuration. + +For a maximal configuration see the Chromium setup: + * [.gn](https://cs.chromium.org/chromium/src/.gn) + * [BUILDCONFIG.gn](https://cs.chromium.org/chromium/src/build/config/BUILDCONFIG.gn) + * [Toolchain setup](https://cs.chromium.org/chromium/src/build/toolchain/) + * [Compiler setup](https://cs.chromium.org/chromium/src/build/config/compiler/BUILD.gn) + +and the Fuchsia setup: + * [.gn](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/.gn) + * [BUILDCONFIG.gn](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/build/config/BUILDCONFIG.gn) + * [Toolchain setup](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/build/toolchain/) + * [Compiler setup](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/build/config/BUILD.gn) + +## Reporting bugs + +If you find a bug, you can see if it is known or report it in the [bug +database](https://bugs.chromium.org/p/gn/issues/list). + +## Sending patches + +GN uses [Gerrit](https://www.gerritcodereview.com/) for code review. The short +version of how to patch is: + + Register at https://gn-review.googlesource.com. + + ... edit code ... + ninja -C out && out/gn_unittests + +Then, to upload a change for review: + + git commit + git cl upload --gerrit + +When revising a change, use: + + git commit --amend + git cl upload --gerrit + +which will add the new changes to the existing code review, rather than creating +a new one. + +We ask that all contributors +[sign Google's Contributor License Agreement](https://cla.developers.google.com/) +(either individual or corporate as appropriate, select 'any other Google +project'). + +## Community + +You may ask questions and follow along with GN's development on Chromium's +[gn-dev@](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev) +Google Group. diff --git a/src/3rdparty/gn/base/atomic_ref_count.h b/src/3rdparty/gn/base/atomic_ref_count.h new file mode 100644 index 00000000000..3ffa017acd3 --- /dev/null +++ b/src/3rdparty/gn/base/atomic_ref_count.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a low level implementation of atomic semantics for reference +// counting. Please use base/memory/ref_counted.h directly instead. + +#ifndef BASE_ATOMIC_REF_COUNT_H_ +#define BASE_ATOMIC_REF_COUNT_H_ + +#include + +namespace base { + +class AtomicRefCount { + public: + constexpr AtomicRefCount() : ref_count_(0) {} + explicit constexpr AtomicRefCount(int initial_value) + : ref_count_(initial_value) {} + + // Increment a reference count. + void Increment() { Increment(1); } + + // Increment a reference count by "increment", which must exceed 0. + void Increment(int increment) { + ref_count_.fetch_add(increment, std::memory_order_relaxed); + } + + // Decrement a reference count, and return whether the result is non-zero. + // Insert barriers to ensure that state written before the reference count + // became zero will be visible to a thread that has just made the count zero. + bool Decrement() { + // TODO(jbroman): Technically this doesn't need to be an acquire operation + // unless the result is 1 (i.e., the ref count did indeed reach zero). + // However, there are toolchain issues that make that not work as well at + // present (notably TSAN doesn't like it). + return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1; + } + + // Return whether the reference count is one. If the reference count is used + // in the conventional way, a refrerence count of 1 implies that the current + // thread owns the reference and no other thread shares it. This call + // performs the test for a reference count of one, and performs the memory + // barrier needed for the owning thread to act on the object, knowing that it + // has exclusive access to the object. + bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; } + + // Return whether the reference count is zero. With conventional object + // referencing counting, the object will be destroyed, so the reference count + // should never be zero. Hence this is generally used for a debug check. + bool IsZero() const { + return ref_count_.load(std::memory_order_acquire) == 0; + } + + // Returns the current reference count (with no barriers). This is subtle, and + // should be used only for debugging. + int SubtleRefCountForDebug() const { + return ref_count_.load(std::memory_order_relaxed); + } + + private: + std::atomic_int ref_count_; +}; + +} // namespace base + +#endif // BASE_ATOMIC_REF_COUNT_H_ diff --git a/src/3rdparty/gn/base/bind.h b/src/3rdparty/gn/base/bind.h new file mode 100644 index 00000000000..71df0fe952a --- /dev/null +++ b/src/3rdparty/gn/base/bind.h @@ -0,0 +1,457 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BIND_H_ +#define BASE_BIND_H_ + +#include + +#include "base/bind_internal.h" + +// ----------------------------------------------------------------------------- +// Usage documentation +// ----------------------------------------------------------------------------- +// +// Overview: +// base::BindOnce() and base::BindRepeating() are helpers for creating +// base::OnceCallback and base::RepeatingCallback objects respectively. +// +// For a runnable object of n-arity, the base::Bind*() family allows partial +// application of the first m arguments. The remaining n - m arguments must be +// passed when invoking the callback with Run(). +// +// // The first argument is bound at callback creation; the remaining +// // two must be passed when calling Run() on the callback object. +// base::OnceCallback cb = base::BindOnce( +// [](short x, int y, long z) { return x * y * z; }, 42); +// +// When binding to a method, the receiver object must also be specified at +// callback creation time. When Run() is invoked, the method will be invoked on +// the specified receiver object. +// +// class C : public base::RefCounted { void F(); }; +// auto instance = base::MakeRefCounted(); +// auto cb = base::BindOnce(&C::F, instance); +// cb.Run(); // Identical to instance->F() +// +// base::Bind is currently a type alias for base::BindRepeating(). In the +// future, we expect to flip this to default to base::BindOnce(). +// +// See //docs/callback.md for the full documentation. +// +// ----------------------------------------------------------------------------- +// Implementation notes +// ----------------------------------------------------------------------------- +// +// If you're reading the implementation, before proceeding further, you should +// read the top comment of base/bind_internal.h for a definition of common +// terms and concepts. + +namespace base { + +namespace internal { + +// IsOnceCallback is a std::true_type if |T| is a OnceCallback. +template +struct IsOnceCallback : std::false_type {}; + +template +struct IsOnceCallback> : std::true_type {}; + +// Helper to assert that parameter |i| of type |Arg| can be bound, which means: +// - |Arg| can be retained internally as |Storage|. +// - |Arg| can be forwarded as |Unwrapped| to |Param|. +template +struct AssertConstructible { + private: + static constexpr bool param_is_forwardable = + std::is_constructible::value; + // Unlike the check for binding into storage below, the check for + // forwardability drops the const qualifier for repeating callbacks. This is + // to try to catch instances where std::move()--which forwards as a const + // reference with repeating callbacks--is used instead of base::Passed(). + static_assert( + param_is_forwardable || + !std::is_constructible&&>::value, + "Bound argument |i| is move-only but will be forwarded by copy. " + "Ensure |Arg| is bound using base::Passed(), not std::move()."); + static_assert( + param_is_forwardable, + "Bound argument |i| of type |Arg| cannot be forwarded as " + "|Unwrapped| to the bound functor, which declares it as |Param|."); + + static constexpr bool arg_is_storable = + std::is_constructible::value; + static_assert(arg_is_storable || + !std::is_constructible&&>::value, + "Bound argument |i| is move-only but will be bound by copy. " + "Ensure |Arg| is mutable and bound using std::move()."); + static_assert(arg_is_storable, + "Bound argument |i| of type |Arg| cannot be converted and " + "bound as |Storage|."); +}; + +// Takes three same-length TypeLists, and applies AssertConstructible for each +// triples. +template +struct AssertBindArgsValidity; + +template +struct AssertBindArgsValidity, + TypeList, + TypeList, + TypeList> + : AssertConstructible, Unwrapped, Params>... { + static constexpr bool ok = true; +}; + +// The implementation of TransformToUnwrappedType below. +template +struct TransformToUnwrappedTypeImpl; + +template +struct TransformToUnwrappedTypeImpl { + using StoredType = std::decay_t; + using ForwardType = StoredType&&; + using Unwrapped = decltype(Unwrap(std::declval())); +}; + +template +struct TransformToUnwrappedTypeImpl { + using StoredType = std::decay_t; + using ForwardType = const StoredType&; + using Unwrapped = decltype(Unwrap(std::declval())); +}; + +// Transform |T| into `Unwrapped` type, which is passed to the target function. +// Example: +// In is_once == true case, +// `int&&` -> `int&&`, +// `const int&` -> `int&&`, +// `OwnedWrapper&` -> `int*&&`. +// In is_once == false case, +// `int&&` -> `const int&`, +// `const int&` -> `const int&`, +// `OwnedWrapper&` -> `int* const &`. +template +using TransformToUnwrappedType = + typename TransformToUnwrappedTypeImpl::Unwrapped; + +// Transforms |Args| into `Unwrapped` types, and packs them into a TypeList. +// If |is_method| is true, tries to dereference the first argument to support +// smart pointers. +template +struct MakeUnwrappedTypeListImpl { + using Type = TypeList...>; +}; + +// Performs special handling for this pointers. +// Example: +// int* -> int*, +// std::unique_ptr -> int*. +template +struct MakeUnwrappedTypeListImpl { + using UnwrappedReceiver = TransformToUnwrappedType; + using Type = TypeList()), + TransformToUnwrappedType...>; +}; + +template +using MakeUnwrappedTypeList = + typename MakeUnwrappedTypeListImpl::Type; + +} // namespace internal + +// Bind as OnceCallback. +template +inline OnceCallback> BindOnce( + Functor&& functor, + Args&&... args) { + static_assert(!internal::IsOnceCallback>() || + (std::is_rvalue_reference() && + !std::is_const>()), + "BindOnce requires non-const rvalue for OnceCallback binding." + " I.e.: base::BindOnce(std::move(callback))."); + + // This block checks if each |args| matches to the corresponding params of the + // target function. This check does not affect the behavior of Bind, but its + // error message should be more readable. + using Helper = internal::BindTypeHelper; + using FunctorTraits = typename Helper::FunctorTraits; + using BoundArgsList = typename Helper::BoundArgsList; + using UnwrappedArgsList = + internal::MakeUnwrappedTypeList; + using BoundParamsList = typename Helper::BoundParamsList; + static_assert(internal::AssertBindArgsValidity< + std::make_index_sequence, BoundArgsList, + UnwrappedArgsList, BoundParamsList>::ok, + "The bound args need to be convertible to the target params."); + + using BindState = internal::MakeBindStateType; + using UnboundRunType = MakeUnboundRunType; + using Invoker = internal::Invoker; + using CallbackType = OnceCallback; + + // Store the invoke func into PolymorphicInvoke before casting it to + // InvokeFuncStorage, so that we can ensure its type matches to + // PolymorphicInvoke, to which CallbackType will cast back. + using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke; + PolymorphicInvoke invoke_func = &Invoker::RunOnce; + + using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage; + return CallbackType(new BindState( + reinterpret_cast(invoke_func), + std::forward(functor), std::forward(args)...)); +} + +// Bind as RepeatingCallback. +template +inline RepeatingCallback> BindRepeating( + Functor&& functor, + Args&&... args) { + static_assert( + !internal::IsOnceCallback>(), + "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move()."); + + // This block checks if each |args| matches to the corresponding params of the + // target function. This check does not affect the behavior of Bind, but its + // error message should be more readable. + using Helper = internal::BindTypeHelper; + using FunctorTraits = typename Helper::FunctorTraits; + using BoundArgsList = typename Helper::BoundArgsList; + using UnwrappedArgsList = + internal::MakeUnwrappedTypeList; + using BoundParamsList = typename Helper::BoundParamsList; + static_assert(internal::AssertBindArgsValidity< + std::make_index_sequence, BoundArgsList, + UnwrappedArgsList, BoundParamsList>::ok, + "The bound args need to be convertible to the target params."); + + using BindState = internal::MakeBindStateType; + using UnboundRunType = MakeUnboundRunType; + using Invoker = internal::Invoker; + using CallbackType = RepeatingCallback; + + // Store the invoke func into PolymorphicInvoke before casting it to + // InvokeFuncStorage, so that we can ensure its type matches to + // PolymorphicInvoke, to which CallbackType will cast back. + using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke; + PolymorphicInvoke invoke_func = &Invoker::Run; + + using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage; + return CallbackType(new BindState( + reinterpret_cast(invoke_func), + std::forward(functor), std::forward(args)...)); +} + +// Unannotated Bind. +// TODO(tzik): Deprecate this and migrate to OnceCallback and +// RepeatingCallback, once they get ready. +template +inline Callback> Bind(Functor&& functor, + Args&&... args) { + return base::BindRepeating(std::forward(functor), + std::forward(args)...); +} + +// Special cases for binding to a base::Callback without extra bound arguments. +template +OnceCallback BindOnce(OnceCallback closure) { + return closure; +} + +template +RepeatingCallback BindRepeating( + RepeatingCallback closure) { + return closure; +} + +template +Callback Bind(Callback closure) { + return closure; +} + +// Unretained() allows Bind() to bind a non-refcounted class, and to disable +// refcounting on arguments that are refcounted objects. +// +// EXAMPLE OF Unretained(): +// +// class Foo { +// public: +// void func() { cout << "Foo:f" << endl; } +// }; +// +// // In some function somewhere. +// Foo foo; +// Closure foo_callback = +// Bind(&Foo::func, Unretained(&foo)); +// foo_callback.Run(); // Prints "Foo:f". +// +// Without the Unretained() wrapper on |&foo|, the above call would fail +// to compile because Foo does not support the AddRef() and Release() methods. +template +static inline internal::UnretainedWrapper Unretained(T* o) { + return internal::UnretainedWrapper(o); +} + +// RetainedRef() accepts a ref counted object and retains a reference to it. +// When the callback is called, the object is passed as a raw pointer. +// +// EXAMPLE OF RetainedRef(): +// +// void foo(RefCountedBytes* bytes) {} +// +// scoped_refptr bytes = ...; +// Closure callback = Bind(&foo, base::RetainedRef(bytes)); +// callback.Run(); +// +// Without RetainedRef, the scoped_refptr would try to implicitly convert to +// a raw pointer and fail compilation: +// +// Closure callback = Bind(&foo, bytes); // ERROR! +template +static inline internal::RetainedRefWrapper RetainedRef(T* o) { + return internal::RetainedRefWrapper(o); +} +template +static inline internal::RetainedRefWrapper RetainedRef(scoped_refptr o) { + return internal::RetainedRefWrapper(std::move(o)); +} + +// ConstRef() allows binding a constant reference to an argument rather +// than a copy. +// +// EXAMPLE OF ConstRef(): +// +// void foo(int arg) { cout << arg << endl } +// +// int n = 1; +// Closure no_ref = Bind(&foo, n); +// Closure has_ref = Bind(&foo, ConstRef(n)); +// +// no_ref.Run(); // Prints "1" +// has_ref.Run(); // Prints "1" +// +// n = 2; +// no_ref.Run(); // Prints "1" +// has_ref.Run(); // Prints "2" +// +// Note that because ConstRef() takes a reference on |n|, |n| must outlive all +// its bound callbacks. +template +static inline internal::ConstRefWrapper ConstRef(const T& o) { + return internal::ConstRefWrapper(o); +} + +// Owned() transfers ownership of an object to the Callback resulting from +// bind; the object will be deleted when the Callback is deleted. +// +// EXAMPLE OF Owned(): +// +// void foo(int* arg) { cout << *arg << endl } +// +// int* pn = new int(1); +// Closure foo_callback = Bind(&foo, Owned(pn)); +// +// foo_callback.Run(); // Prints "1" +// foo_callback.Run(); // Prints "1" +// *n = 2; +// foo_callback.Run(); // Prints "2" +// +// foo_callback.Reset(); // |pn| is deleted. Also will happen when +// // |foo_callback| goes out of scope. +// +// Without Owned(), someone would have to know to delete |pn| when the last +// reference to the Callback is deleted. +template +static inline internal::OwnedWrapper Owned(T* o) { + return internal::OwnedWrapper(o); +} + +// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr) +// through a Callback. Logically, this signifies a destructive transfer of +// the state of the argument into the target function. Invoking +// Callback::Run() twice on a Callback that was created with a Passed() +// argument will CHECK() because the first invocation would have already +// transferred ownership to the target function. +// +// Note that Passed() is not necessary with BindOnce(), as std::move() does the +// same thing. Avoid Passed() in favor of std::move() with BindOnce(). +// +// EXAMPLE OF Passed(): +// +// void TakesOwnership(std::unique_ptr arg) { } +// std::unique_ptr CreateFoo() { return std::make_unique(); +// } +// +// auto f = std::make_unique(); +// +// // |cb| is given ownership of Foo(). |f| is now NULL. +// // You can use std::move(f) in place of &f, but it's more verbose. +// Closure cb = Bind(&TakesOwnership, Passed(&f)); +// +// // Run was never called so |cb| still owns Foo() and deletes +// // it on Reset(). +// cb.Reset(); +// +// // |cb| is given a new Foo created by CreateFoo(). +// cb = Bind(&TakesOwnership, Passed(CreateFoo())); +// +// // |arg| in TakesOwnership() is given ownership of Foo(). |cb| +// // no longer owns Foo() and, if reset, would not delete Foo(). +// cb.Run(); // Foo() is now transferred to |arg| and deleted. +// cb.Run(); // This CHECK()s since Foo() already been used once. +// +// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and +// is best suited for use with the return value of a function or other temporary +// rvalues. The second takes a pointer to the scoper and is just syntactic sugar +// to avoid having to write Passed(std::move(scoper)). +// +// Both versions of Passed() prevent T from being an lvalue reference. The first +// via use of enable_if, and the second takes a T* which will not bind to T&. +template ::value>* = nullptr> +static inline internal::PassedWrapper Passed(T&& scoper) { + return internal::PassedWrapper(std::move(scoper)); +} +template +static inline internal::PassedWrapper Passed(T* scoper) { + return internal::PassedWrapper(std::move(*scoper)); +} + +// IgnoreResult() is used to adapt a function or Callback with a return type to +// one with a void return. This is most useful if you have a function with, +// say, a pesky ignorable bool return that you want to use with PostTask or +// something else that expect a Callback with a void return. +// +// EXAMPLE OF IgnoreResult(): +// +// int DoSomething(int arg) { cout << arg << endl; } +// +// // Assign to a Callback with a void return type. +// Callback cb = Bind(IgnoreResult(&DoSomething)); +// cb->Run(1); // Prints "1". +// +// // Prints "1" on |ml|. +// ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1); +template +static inline internal::IgnoreResultHelper IgnoreResult(T data) { + return internal::IgnoreResultHelper(std::move(data)); +} + +} // namespace base + +#endif // BASE_BIND_H_ diff --git a/src/3rdparty/gn/base/bind_internal.h b/src/3rdparty/gn/base/bind_internal.h new file mode 100644 index 00000000000..0dc59e60c9a --- /dev/null +++ b/src/3rdparty/gn/base/bind_internal.h @@ -0,0 +1,912 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BIND_INTERNAL_H_ +#define BASE_BIND_INTERNAL_H_ + +#include + +#include +#include + +#include "base/callback_internal.h" +#include "base/memory/raw_scoped_refptr_mismatch_checker.h" +#include "base/memory/weak_ptr.h" +#include "base/template_util.h" +#include "util/build_config.h" + +// See base/callback.h for user documentation. +// +// +// CONCEPTS: +// Functor -- A movable type representing something that should be called. +// All function pointers and Callback<> are functors even if the +// invocation syntax differs. +// RunType -- A function type (as opposed to function _pointer_ type) for +// a Callback<>::Run(). Usually just a convenience typedef. +// (Bound)Args -- A set of types that stores the arguments. +// +// Types: +// ForceVoidReturn<> -- Helper class for translating function signatures to +// equivalent forms with a "void" return type. +// FunctorTraits<> -- Type traits used to determine the correct RunType and +// invocation manner for a Functor. This is where function +// signature adapters are applied. +// InvokeHelper<> -- Take a Functor + arguments and actully invokes it. +// Handle the differing syntaxes needed for WeakPtr<> +// support. This is separate from Invoker to avoid creating +// multiple version of Invoker<>. +// Invoker<> -- Unwraps the curried parameters and executes the Functor. +// BindState<> -- Stores the curried parameters, and is the main entry point +// into the Bind() system. + +namespace base { + +template +struct IsWeakReceiver; + +template +struct BindUnwrapTraits; + +template +struct CallbackCancellationTraits; + +namespace internal { + +template +struct FunctorTraits; + +template +class UnretainedWrapper { + public: + explicit UnretainedWrapper(T* o) : ptr_(o) {} + T* get() const { return ptr_; } + + private: + T* ptr_; +}; + +template +class ConstRefWrapper { + public: + explicit ConstRefWrapper(const T& o) : ptr_(&o) {} + const T& get() const { return *ptr_; } + + private: + const T* ptr_; +}; + +template +class RetainedRefWrapper { + public: + explicit RetainedRefWrapper(T* o) : ptr_(o) {} + explicit RetainedRefWrapper(scoped_refptr o) : ptr_(std::move(o)) {} + T* get() const { return ptr_.get(); } + + private: + scoped_refptr ptr_; +}; + +template +struct IgnoreResultHelper { + explicit IgnoreResultHelper(T functor) : functor_(std::move(functor)) {} + explicit operator bool() const { return !!functor_; } + + T functor_; +}; + +// An alternate implementation is to avoid the destructive copy, and instead +// specialize ParamTraits<> for OwnedWrapper<> to change the StorageType to +// a class that is essentially a std::unique_ptr<>. +// +// The current implementation has the benefit though of leaving ParamTraits<> +// fully in callback_internal.h as well as avoiding type conversions during +// storage. +template +class OwnedWrapper { + public: + explicit OwnedWrapper(T* o) : ptr_(o) {} + ~OwnedWrapper() { delete ptr_; } + T* get() const { return ptr_; } + OwnedWrapper(OwnedWrapper&& other) { + ptr_ = other.ptr_; + other.ptr_ = NULL; + } + + private: + mutable T* ptr_; +}; + +// PassedWrapper is a copyable adapter for a scoper that ignores const. +// +// It is needed to get around the fact that Bind() takes a const reference to +// all its arguments. Because Bind() takes a const reference to avoid +// unnecessary copies, it is incompatible with movable-but-not-copyable +// types; doing a destructive "move" of the type into Bind() would violate +// the const correctness. +// +// This conundrum cannot be solved without either C++11 rvalue references or +// a O(2^n) blowup of Bind() templates to handle each combination of regular +// types and movable-but-not-copyable types. Thus we introduce a wrapper type +// that is copyable to transmit the correct type information down into +// BindState<>. Ignoring const in this type makes sense because it is only +// created when we are explicitly trying to do a destructive move. +// +// Two notes: +// 1) PassedWrapper supports any type that has a move constructor, however +// the type will need to be specifically whitelisted in order for it to be +// bound to a Callback. We guard this explicitly at the call of Passed() +// to make for clear errors. Things not given to Passed() will be forwarded +// and stored by value which will not work for general move-only types. +// 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL" +// scoper to a Callback and allow the Callback to execute once. +template +class PassedWrapper { + public: + explicit PassedWrapper(T&& scoper) + : is_valid_(true), scoper_(std::move(scoper)) {} + PassedWrapper(PassedWrapper&& other) + : is_valid_(other.is_valid_), scoper_(std::move(other.scoper_)) {} + T Take() const { + CHECK(is_valid_); + is_valid_ = false; + return std::move(scoper_); + } + + private: + mutable bool is_valid_; + mutable T scoper_; +}; + +template +using Unwrapper = BindUnwrapTraits>; + +template +decltype(auto) Unwrap(T&& o) { + return Unwrapper::Unwrap(std::forward(o)); +} + +// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a +// method. It is used internally by Bind() to select the correct +// InvokeHelper that will no-op itself in the event the WeakPtr<> for +// the target object is invalidated. +// +// The first argument should be the type of the object that will be received by +// the method. +template +struct IsWeakMethod : std::false_type {}; + +template +struct IsWeakMethod : IsWeakReceiver {}; + +// Packs a list of types to hold them in a single type. +template +struct TypeList {}; + +// Used for DropTypeListItem implementation. +template +struct DropTypeListItemImpl; + +// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. +template +struct DropTypeListItemImpl> + : DropTypeListItemImpl> {}; + +template +struct DropTypeListItemImpl<0, TypeList> { + using Type = TypeList; +}; + +template <> +struct DropTypeListItemImpl<0, TypeList<>> { + using Type = TypeList<>; +}; + +// A type-level function that drops |n| list item from given TypeList. +template +using DropTypeListItem = typename DropTypeListItemImpl::Type; + +// Used for TakeTypeListItem implementation. +template +struct TakeTypeListItemImpl; + +// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. +template +struct TakeTypeListItemImpl, Accum...> + : TakeTypeListItemImpl, Accum..., T> {}; + +template +struct TakeTypeListItemImpl<0, TypeList, Accum...> { + using Type = TypeList; +}; + +template +struct TakeTypeListItemImpl<0, TypeList<>, Accum...> { + using Type = TypeList; +}; + +// A type-level function that takes first |n| list item from given TypeList. +// E.g. TakeTypeListItem<3, TypeList> is evaluated to +// TypeList. +template +using TakeTypeListItem = typename TakeTypeListItemImpl::Type; + +// Used for ConcatTypeLists implementation. +template +struct ConcatTypeListsImpl; + +template +struct ConcatTypeListsImpl, TypeList> { + using Type = TypeList; +}; + +// A type-level function that concats two TypeLists. +template +using ConcatTypeLists = typename ConcatTypeListsImpl::Type; + +// Used for MakeFunctionType implementation. +template +struct MakeFunctionTypeImpl; + +template +struct MakeFunctionTypeImpl> { + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R Type(Args...); +}; + +// A type-level function that constructs a function type that has |R| as its +// return type and has TypeLists items as its arguments. +template +using MakeFunctionType = typename MakeFunctionTypeImpl::Type; + +// Used for ExtractArgs and ExtractReturnType. +template +struct ExtractArgsImpl; + +template +struct ExtractArgsImpl { + using ReturnType = R; + using ArgsList = TypeList; +}; + +// A type-level function that extracts function arguments into a TypeList. +// E.g. ExtractArgs is evaluated to TypeList. +template +using ExtractArgs = typename ExtractArgsImpl::ArgsList; + +// A type-level function that extracts the return type of a function. +// E.g. ExtractReturnType is evaluated to R. +template +using ExtractReturnType = typename ExtractArgsImpl::ReturnType; + +template +struct ExtractCallableRunTypeImpl; + +template +struct ExtractCallableRunTypeImpl { + using Type = R(Args...); +}; + +template +struct ExtractCallableRunTypeImpl { + using Type = R(Args...); +}; + +// Evaluated to RunType of the given callable type. +// Example: +// auto f = [](int, char*) { return 0.1; }; +// ExtractCallableRunType +// is evaluated to +// double(int, char*); +template +using ExtractCallableRunType = + typename ExtractCallableRunTypeImpl::Type; + +// IsCallableObject is std::true_type if |Functor| has operator(). +// Otherwise, it's std::false_type. +// Example: +// IsCallableObject::value is false. +// +// struct Foo {}; +// IsCallableObject::value is false. +// +// int i = 0; +// auto f = [i]() {}; +// IsCallableObject::value is false. +template +struct IsCallableObject : std::false_type {}; + +template +struct IsCallableObject> + : std::true_type {}; + +// HasRefCountedTypeAsRawPtr selects true_type when any of the |Args| is a raw +// pointer to a RefCounted type. +// Implementation note: This non-specialized case handles zero-arity case only. +// Non-zero-arity cases should be handled by the specialization below. +template +struct HasRefCountedTypeAsRawPtr : std::false_type {}; + +// Implementation note: Select true_type if the first parameter is a raw pointer +// to a RefCounted type. Otherwise, skip the first parameter and check rest of +// parameters recursively. +template +struct HasRefCountedTypeAsRawPtr + : std::conditional_t::value, + std::true_type, + HasRefCountedTypeAsRawPtr> {}; + +// ForceVoidReturn<> +// +// Set of templates that support forcing the function return type to void. +template +struct ForceVoidReturn; + +template +struct ForceVoidReturn { + using RunType = void(Args...); +}; + +// FunctorTraits<> +// +// See description at top of file. +template +struct FunctorTraits; + +// For empty callable types. +// This specialization is intended to allow binding captureless lambdas by +// base::Bind(), based on the fact that captureless lambdas are empty while +// capturing lambdas are not. This also allows any functors as far as it's an +// empty class. +// Example: +// +// // Captureless lambdas are allowed. +// []() {return 42;}; +// +// // Capturing lambdas are *not* allowed. +// int x; +// [x]() {return x;}; +// +// // Any empty class with operator() is allowed. +// struct Foo { +// void operator()() const {} +// // No non-static member variable and no virtual functions. +// }; +template +struct FunctorTraits::value && + std::is_empty::value>> { + using RunType = ExtractCallableRunType; + static constexpr bool is_method = false; + static constexpr bool is_nullable = false; + + template + static ExtractReturnType Invoke(RunFunctor&& functor, + RunArgs&&... args) { + return std::forward(functor)(std::forward(args)...); + } +}; + +// For functions. +template +struct FunctorTraits { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(Function&& function, RunArgs&&... args) { + return std::forward(function)(std::forward(args)...); + } +}; + +#if defined(OS_WIN) && !defined(ARCH_CPU_X86_64) + +// For functions. +template +struct FunctorTraits { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(R(__stdcall* function)(Args...), RunArgs&&... args) { + return function(std::forward(args)...); + } +}; + +// For functions. +template +struct FunctorTraits { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(R(__fastcall* function)(Args...), RunArgs&&... args) { + return function(std::forward(args)...); + } +}; + +#endif // defined(OS_WIN) && !defined(ARCH_CPU_X86_64) + +// For methods. +template +struct FunctorTraits { + using RunType = R(Receiver*, Args...); + static constexpr bool is_method = true; + static constexpr bool is_nullable = true; + + template + static R Invoke(Method method, + ReceiverPtr&& receiver_ptr, + RunArgs&&... args) { + return ((*receiver_ptr).*method)(std::forward(args)...); + } +}; + +// For const methods. +template +struct FunctorTraits { + using RunType = R(const Receiver*, Args...); + static constexpr bool is_method = true; + static constexpr bool is_nullable = true; + + template + static R Invoke(Method method, + ReceiverPtr&& receiver_ptr, + RunArgs&&... args) { + return ((*receiver_ptr).*method)(std::forward(args)...); + } +}; + +#ifdef __cpp_noexcept_function_type +// noexcept makes a distinct function type in C++17. +// I.e. `void(*)()` and `void(*)() noexcept` are same in pre-C++17, and +// different in C++17. +template +struct FunctorTraits : FunctorTraits { +}; + +template +struct FunctorTraits + : FunctorTraits {}; + +template +struct FunctorTraits + : FunctorTraits {}; +#endif + +// For IgnoreResults. +template +struct FunctorTraits> : FunctorTraits { + using RunType = + typename ForceVoidReturn::RunType>::RunType; + + template + static void Invoke(IgnoreResultType&& ignore_result_helper, + RunArgs&&... args) { + FunctorTraits::Invoke( + std::forward(ignore_result_helper).functor_, + std::forward(args)...); + } +}; + +// For OnceCallbacks. +template +struct FunctorTraits> { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(CallbackType&& callback, RunArgs&&... args) { + DCHECK(!callback.is_null()); + return std::forward(callback).Run( + std::forward(args)...); + } +}; + +// For RepeatingCallbacks. +template +struct FunctorTraits> { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(CallbackType&& callback, RunArgs&&... args) { + DCHECK(!callback.is_null()); + return std::forward(callback).Run( + std::forward(args)...); + } +}; + +template +using MakeFunctorTraits = FunctorTraits>; + +// InvokeHelper<> +// +// There are 2 logical InvokeHelper<> specializations: normal, WeakCalls. +// +// The normal type just calls the underlying runnable. +// +// WeakCalls need special syntax that is applied to the first argument to check +// if they should no-op themselves. +template +struct InvokeHelper; + +template +struct InvokeHelper { + template + static inline ReturnType MakeItSo(Functor&& functor, RunArgs&&... args) { + using Traits = MakeFunctorTraits; + return Traits::Invoke(std::forward(functor), + std::forward(args)...); + } +}; + +template +struct InvokeHelper { + // WeakCalls are only supported for functions with a void return type. + // Otherwise, the function result would be undefined if the the WeakPtr<> + // is invalidated. + static_assert(std::is_void::value, + "weak_ptrs can only bind to methods without return values"); + + template + static inline void MakeItSo(Functor&& functor, + BoundWeakPtr&& weak_ptr, + RunArgs&&... args) { + if (!weak_ptr) + return; + using Traits = MakeFunctorTraits; + Traits::Invoke(std::forward(functor), + std::forward(weak_ptr), + std::forward(args)...); + } +}; + +// Invoker<> +// +// See description at the top of the file. +template +struct Invoker; + +template +struct Invoker { + static R RunOnce(BindStateBase* base, + PassingTraitsType... unbound_args) { + // Local references to make debugger stepping easier. If in a debugger, + // you really want to warp ahead and step through the + // InvokeHelper<>::MakeItSo() call below. + StorageType* storage = static_cast(base); + static constexpr size_t num_bound_args = + std::tuple_sizebound_args_)>::value; + return RunImpl(std::move(storage->functor_), + std::move(storage->bound_args_), + std::make_index_sequence(), + std::forward(unbound_args)...); + } + + static R Run(BindStateBase* base, + PassingTraitsType... unbound_args) { + // Local references to make debugger stepping easier. If in a debugger, + // you really want to warp ahead and step through the + // InvokeHelper<>::MakeItSo() call below. + const StorageType* storage = static_cast(base); + static constexpr size_t num_bound_args = + std::tuple_sizebound_args_)>::value; + return RunImpl(storage->functor_, storage->bound_args_, + std::make_index_sequence(), + std::forward(unbound_args)...); + } + + private: + template + static inline R RunImpl(Functor&& functor, + BoundArgsTuple&& bound, + std::index_sequence, + UnboundArgs&&... unbound_args) { + static constexpr bool is_method = MakeFunctorTraits::is_method; + + using DecayedArgsTuple = std::decay_t; + static constexpr bool is_weak_call = + IsWeakMethod...>(); + + return InvokeHelper::MakeItSo( + std::forward(functor), + Unwrap(std::get(std::forward(bound)))..., + std::forward(unbound_args)...); + } +}; + +// Extracts necessary type info from Functor and BoundArgs. +// Used to implement MakeUnboundRunType, BindOnce and BindRepeating. +template +struct BindTypeHelper { + static constexpr size_t num_bounds = sizeof...(BoundArgs); + using FunctorTraits = MakeFunctorTraits; + + // Example: + // When Functor is `double (Foo::*)(int, const std::string&)`, and BoundArgs + // is a template pack of `Foo*` and `int16_t`: + // - RunType is `double(Foo*, int, const std::string&)`, + // - ReturnType is `double`, + // - RunParamsList is `TypeList`, + // - BoundParamsList is `TypeList`, + // - UnboundParamsList is `TypeList`, + // - BoundArgsList is `TypeList`, + // - UnboundRunType is `double(const std::string&)`. + using RunType = typename FunctorTraits::RunType; + using ReturnType = ExtractReturnType; + + using RunParamsList = ExtractArgs; + using BoundParamsList = TakeTypeListItem; + using UnboundParamsList = DropTypeListItem; + + using BoundArgsList = TypeList; + + using UnboundRunType = MakeFunctionType; +}; + +template +std::enable_if_t::is_nullable, bool> IsNull( + const Functor& functor) { + return !functor; +} + +template +std::enable_if_t::is_nullable, bool> IsNull( + const Functor&) { + return false; +} + +// Used by ApplyCancellationTraits below. +template +bool ApplyCancellationTraitsImpl(const Functor& functor, + const BoundArgsTuple& bound_args, + std::index_sequence) { + return CallbackCancellationTraits::IsCancelled( + functor, std::get(bound_args)...); +} + +// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns +// true if the callback |base| represents is canceled. +template +bool ApplyCancellationTraits(const BindStateBase* base) { + const BindStateType* storage = static_cast(base); + static constexpr size_t num_bound_args = + std::tuple_sizebound_args_)>::value; + return ApplyCancellationTraitsImpl( + storage->functor_, storage->bound_args_, + std::make_index_sequence()); +}; + +// BindState<> +// +// This stores all the state passed into Bind(). +template +struct BindState final : BindStateBase { + using IsCancellable = std::integral_constant< + bool, + CallbackCancellationTraits>::is_cancellable>; + + template + explicit BindState(BindStateBase::InvokeFuncStorage invoke_func, + ForwardFunctor&& functor, + ForwardBoundArgs&&... bound_args) + // IsCancellable is std::false_type if + // CallbackCancellationTraits<>::IsCancelled returns always false. + // Otherwise, it's std::true_type. + : BindState(IsCancellable{}, + invoke_func, + std::forward(functor), + std::forward(bound_args)...) {} + + Functor functor_; + std::tuple bound_args_; + + private: + template + explicit BindState(std::true_type, + BindStateBase::InvokeFuncStorage invoke_func, + ForwardFunctor&& functor, + ForwardBoundArgs&&... bound_args) + : BindStateBase(invoke_func, + &Destroy, + &ApplyCancellationTraits), + functor_(std::forward(functor)), + bound_args_(std::forward(bound_args)...) { + DCHECK(!IsNull(functor_)); + } + + template + explicit BindState(std::false_type, + BindStateBase::InvokeFuncStorage invoke_func, + ForwardFunctor&& functor, + ForwardBoundArgs&&... bound_args) + : BindStateBase(invoke_func, &Destroy), + functor_(std::forward(functor)), + bound_args_(std::forward(bound_args)...) { + DCHECK(!IsNull(functor_)); + } + + ~BindState() = default; + + static void Destroy(const BindStateBase* self) { + delete static_cast(self); + } +}; + +// Used to implement MakeBindStateType. +template +struct MakeBindStateTypeImpl; + +template +struct MakeBindStateTypeImpl { + static_assert(!HasRefCountedTypeAsRawPtr...>::value, + "A parameter is a refcounted type and needs scoped_refptr."); + using Type = BindState, std::decay_t...>; +}; + +template +struct MakeBindStateTypeImpl { + using Type = BindState>; +}; + +template +struct MakeBindStateTypeImpl { + private: + using DecayedReceiver = std::decay_t; + + static_assert(!std::is_array>::value, + "First bound argument to a method cannot be an array."); + static_assert( + !std::is_pointer::value || + IsRefCountedType>::value, + "Receivers may not be raw pointers. If using a raw pointer here is safe" + " and has no lifetime concerns, use base::Unretained() and document why" + " it's safe."); + static_assert(!HasRefCountedTypeAsRawPtr...>::value, + "A parameter is a refcounted type and needs scoped_refptr."); + + public: + using Type = BindState< + std::decay_t, + std::conditional_t::value, + scoped_refptr>, + DecayedReceiver>, + std::decay_t...>; +}; + +template +using MakeBindStateType = + typename MakeBindStateTypeImpl::is_method, + Functor, + BoundArgs...>::Type; + +} // namespace internal + +// An injection point to control |this| pointer behavior on a method invocation. +// If IsWeakReceiver<> is true_type for |T| and |T| is used for a receiver of a +// method, base::Bind cancels the method invocation if the receiver is tested as +// false. +// E.g. Foo::bar() is not called: +// struct Foo : base::SupportsWeakPtr { +// void bar() {} +// }; +// +// WeakPtr oo = nullptr; +// base::Bind(&Foo::bar, oo).Run(); +template +struct IsWeakReceiver : std::false_type {}; + +template +struct IsWeakReceiver> : IsWeakReceiver {}; + +template +struct IsWeakReceiver> : std::true_type {}; + +// An injection point to control how bound objects passed to the target +// function. BindUnwrapTraits<>::Unwrap() is called for each bound objects right +// before the target function is invoked. +template +struct BindUnwrapTraits { + template + static T&& Unwrap(T&& o) { + return std::forward(o); + } +}; + +template +struct BindUnwrapTraits> { + static T* Unwrap(const internal::UnretainedWrapper& o) { return o.get(); } +}; + +template +struct BindUnwrapTraits> { + static const T& Unwrap(const internal::ConstRefWrapper& o) { + return o.get(); + } +}; + +template +struct BindUnwrapTraits> { + static T* Unwrap(const internal::RetainedRefWrapper& o) { return o.get(); } +}; + +template +struct BindUnwrapTraits> { + static T* Unwrap(const internal::OwnedWrapper& o) { return o.get(); } +}; + +template +struct BindUnwrapTraits> { + static T Unwrap(const internal::PassedWrapper& o) { return o.Take(); } +}; + +// CallbackCancellationTraits allows customization of Callback's cancellation +// semantics. By default, callbacks are not cancellable. A specialization should +// set is_cancellable = true and implement an IsCancelled() that returns if the +// callback should be cancelled. +template +struct CallbackCancellationTraits { + static constexpr bool is_cancellable = false; +}; + +// Specialization for method bound to weak pointer receiver. +template +struct CallbackCancellationTraits< + Functor, + std::tuple, + std::enable_if_t< + internal::IsWeakMethod::is_method, + BoundArgs...>::value>> { + static constexpr bool is_cancellable = true; + + template + static bool IsCancelled(const Functor&, + const Receiver& receiver, + const Args&...) { + return !receiver; + } +}; + +// Specialization for a nested bind. +template +struct CallbackCancellationTraits, + std::tuple> { + static constexpr bool is_cancellable = true; + + template + static bool IsCancelled(const Functor& functor, const BoundArgs&...) { + return functor.IsCancelled(); + } +}; + +template +struct CallbackCancellationTraits, + std::tuple> { + static constexpr bool is_cancellable = true; + + template + static bool IsCancelled(const Functor& functor, const BoundArgs&...) { + return functor.IsCancelled(); + } +}; + +// Returns a RunType of bound functor. +// E.g. MakeUnboundRunType is evaluated to R(C). +template +using MakeUnboundRunType = + typename internal::BindTypeHelper::UnboundRunType; + +} // namespace base + +#endif // BASE_BIND_INTERNAL_H_ diff --git a/src/3rdparty/gn/base/callback.h b/src/3rdparty/gn/base/callback.h new file mode 100644 index 00000000000..1b84f403969 --- /dev/null +++ b/src/3rdparty/gn/base/callback.h @@ -0,0 +1,142 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// NOTE: Header files that do not require the full definition of Callback or +// Closure should #include "base/callback_forward.h" instead of this file. + +#ifndef BASE_CALLBACK_H_ +#define BASE_CALLBACK_H_ + +#include "base/callback_forward.h" +#include "base/callback_internal.h" + +// ----------------------------------------------------------------------------- +// Usage documentation +// ----------------------------------------------------------------------------- +// +// Overview: +// A callback is similar in concept to a function pointer: it wraps a runnable +// object such as a function, method, lambda, or even another callback, allowing +// the runnable object to be invoked later via the callback object. +// +// Unlike function pointers, callbacks are created with base::BindOnce() or +// base::BindRepeating() and support partial function application. +// +// A base::OnceCallback may be Run() at most once; a base::RepeatingCallback may +// be Run() any number of times. |is_null()| is guaranteed to return true for a +// moved-from callback. +// +// // The lambda takes two arguments, but the first argument |x| is bound at +// // callback creation. +// base::OnceCallback cb = base::BindOnce([] (int x, int y) { +// return x + y; +// }, 1); +// // Run() only needs the remaining unbound argument |y|. +// printf("1 + 2 = %d\n", std::move(cb).Run(2)); // Prints 3 +// printf("cb is null? %s\n", +// cb.is_null() ? "true" : "false"); // Prints true +// std::move(cb).Run(2); // Crashes since |cb| has already run. +// +// Callbacks also support cancellation. A common use is binding the receiver +// object as a WeakPtr. If that weak pointer is invalidated, calling Run() +// will be a no-op. Note that |is_cancelled()| and |is_null()| are distinct: +// simply cancelling a callback will not also make it null. +// +// base::Callback is currently a type alias for base::RepeatingCallback. In the +// future, we expect to flip this to default to base::OnceCallback. +// +// See //docs/callback.md for the full documentation. + +namespace base { + +template +class OnceCallback : public internal::CallbackBase { + public: + using RunType = R(Args...); + using PolymorphicInvoke = R (*)(internal::BindStateBase*, + internal::PassingTraitsType...); + + constexpr OnceCallback() = default; + + explicit OnceCallback(internal::BindStateBase* bind_state) + : internal::CallbackBase(bind_state) {} + + OnceCallback(const OnceCallback&) = delete; + OnceCallback& operator=(const OnceCallback&) = delete; + + OnceCallback(OnceCallback&&) noexcept = default; + OnceCallback& operator=(OnceCallback&&) noexcept = default; + + OnceCallback(RepeatingCallback other) + : internal::CallbackBase(std::move(other)) {} + + OnceCallback& operator=(RepeatingCallback other) { + static_cast(*this) = std::move(other); + return *this; + } + + bool Equals(const OnceCallback& other) const { return EqualsInternal(other); } + + R Run(Args... args) const& { + static_assert(!sizeof(*this), + "OnceCallback::Run() may only be invoked on a non-const " + "rvalue, i.e. std::move(callback).Run()."); + NOTREACHED(); + } + + R Run(Args... args) && { + // Move the callback instance into a local variable before the invocation, + // that ensures the internal state is cleared after the invocation. + // It's not safe to touch |this| after the invocation, since running the + // bound function may destroy |this|. + OnceCallback cb = std::move(*this); + PolymorphicInvoke f = + reinterpret_cast(cb.polymorphic_invoke()); + return f(cb.bind_state_.get(), std::forward(args)...); + } +}; + +template +class RepeatingCallback : public internal::CallbackBaseCopyable { + public: + using RunType = R(Args...); + using PolymorphicInvoke = R (*)(internal::BindStateBase*, + internal::PassingTraitsType...); + + constexpr RepeatingCallback() = default; + + explicit RepeatingCallback(internal::BindStateBase* bind_state) + : internal::CallbackBaseCopyable(bind_state) {} + + // Copyable and movable. + RepeatingCallback(const RepeatingCallback&) = default; + RepeatingCallback& operator=(const RepeatingCallback&) = default; + RepeatingCallback(RepeatingCallback&&) noexcept = default; + RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default; + + bool Equals(const RepeatingCallback& other) const { + return EqualsInternal(other); + } + + R Run(Args... args) const& { + PolymorphicInvoke f = + reinterpret_cast(this->polymorphic_invoke()); + return f(this->bind_state_.get(), std::forward(args)...); + } + + R Run(Args... args) && { + // Move the callback instance into a local variable before the invocation, + // that ensures the internal state is cleared after the invocation. + // It's not safe to touch |this| after the invocation, since running the + // bound function may destroy |this|. + RepeatingCallback cb = std::move(*this); + PolymorphicInvoke f = + reinterpret_cast(cb.polymorphic_invoke()); + return f(cb.bind_state_.get(), std::forward(args)...); + } +}; + +} // namespace base + +#endif // BASE_CALLBACK_H_ diff --git a/src/3rdparty/gn/base/callback_forward.h b/src/3rdparty/gn/base/callback_forward.h new file mode 100644 index 00000000000..f1851c4fbbf --- /dev/null +++ b/src/3rdparty/gn/base/callback_forward.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CALLBACK_FORWARD_H_ +#define BASE_CALLBACK_FORWARD_H_ + +namespace base { + +template +class OnceCallback; + +template +class RepeatingCallback; + +template +using Callback = RepeatingCallback; + +// Syntactic sugar to make Callback easier to declare since it +// will be used in a lot of APIs with delayed execution. +using OnceClosure = OnceCallback; +using RepeatingClosure = RepeatingCallback; +using Closure = Callback; + +} // namespace base + +#endif // BASE_CALLBACK_FORWARD_H_ diff --git a/src/3rdparty/gn/base/callback_internal.cc b/src/3rdparty/gn/base/callback_internal.cc new file mode 100644 index 00000000000..c52d8afaba2 --- /dev/null +++ b/src/3rdparty/gn/base/callback_internal.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/callback_internal.h" + +#include "base/logging.h" + +namespace base { +namespace internal { + +namespace { + +bool ReturnFalse(const BindStateBase*) { + return false; +} + +} // namespace + +void BindStateBaseRefCountTraits::Destruct(const BindStateBase* bind_state) { + bind_state->destructor_(bind_state); +} + +BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*)) + : BindStateBase(polymorphic_invoke, destructor, &ReturnFalse) {} + +BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*), + bool (*is_cancelled)(const BindStateBase*)) + : polymorphic_invoke_(polymorphic_invoke), + destructor_(destructor), + is_cancelled_(is_cancelled) {} + +CallbackBase::CallbackBase(CallbackBase&& c) noexcept = default; +CallbackBase& CallbackBase::operator=(CallbackBase&& c) noexcept = default; +CallbackBase::CallbackBase(const CallbackBaseCopyable& c) + : bind_state_(c.bind_state_) {} + +CallbackBase& CallbackBase::operator=(const CallbackBaseCopyable& c) { + bind_state_ = c.bind_state_; + return *this; +} + +CallbackBase::CallbackBase(CallbackBaseCopyable&& c) noexcept + : bind_state_(std::move(c.bind_state_)) {} + +CallbackBase& CallbackBase::operator=(CallbackBaseCopyable&& c) noexcept { + bind_state_ = std::move(c.bind_state_); + return *this; +} + +void CallbackBase::Reset() { + // NULL the bind_state_ last, since it may be holding the last ref to whatever + // object owns us, and we may be deleted after that. + bind_state_ = nullptr; +} + +bool CallbackBase::IsCancelled() const { + DCHECK(bind_state_); + return bind_state_->IsCancelled(); +} + +bool CallbackBase::EqualsInternal(const CallbackBase& other) const { + return bind_state_ == other.bind_state_; +} + +CallbackBase::CallbackBase(BindStateBase* bind_state) + : bind_state_(bind_state ? AdoptRef(bind_state) : nullptr) { + DCHECK(!bind_state_.get() || bind_state_->HasOneRef()); +} + +CallbackBase::~CallbackBase() = default; + +CallbackBaseCopyable::CallbackBaseCopyable(const CallbackBaseCopyable& c) + : CallbackBase(nullptr) { + bind_state_ = c.bind_state_; +} + +CallbackBaseCopyable::CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept = + default; + +CallbackBaseCopyable& CallbackBaseCopyable::operator=( + const CallbackBaseCopyable& c) { + bind_state_ = c.bind_state_; + return *this; +} + +CallbackBaseCopyable& CallbackBaseCopyable::operator=( + CallbackBaseCopyable&& c) noexcept = default; + +} // namespace internal +} // namespace base diff --git a/src/3rdparty/gn/base/callback_internal.h b/src/3rdparty/gn/base/callback_internal.h new file mode 100644 index 00000000000..7e5180f317a --- /dev/null +++ b/src/3rdparty/gn/base/callback_internal.h @@ -0,0 +1,172 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains utility functions and classes that help the +// implementation, and management of the Callback objects. + +#ifndef BASE_CALLBACK_INTERNAL_H_ +#define BASE_CALLBACK_INTERNAL_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { + +struct FakeBindState; + +namespace internal { + +class CallbackBase; +class CallbackBaseCopyable; + +class BindStateBase; + +template +struct BindState; + +struct BindStateBaseRefCountTraits { + static void Destruct(const BindStateBase*); +}; + +template ::value> +struct PassingTraits; + +template +struct PassingTraits { + using Type = T&&; +}; + +template +struct PassingTraits { + using Type = T; +}; + +template +using PassingTraitsType = typename PassingTraits::Type; + +// BindStateBase is used to provide an opaque handle that the Callback +// class can use to represent a function object with bound arguments. It +// behaves as an existential type that is used by a corresponding +// DoInvoke function to perform the function execution. This allows +// us to shield the Callback class from the types of the bound argument via +// "type erasure." +// At the base level, the only task is to add reference counting data. Don't use +// RefCountedThreadSafe since it requires the destructor to be a virtual method. +// Creating a vtable for every BindState template instantiation results in a lot +// of bloat. Its only task is to call the destructor which can be done with a +// function pointer. +class BindStateBase + : public RefCountedThreadSafe { + public: + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); + + using InvokeFuncStorage = void (*)(); + + private: + BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*)); + BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*), + bool (*is_cancelled)(const BindStateBase*)); + + ~BindStateBase() = default; + + friend struct BindStateBaseRefCountTraits; + friend class RefCountedThreadSafe; + + friend class CallbackBase; + friend class CallbackBaseCopyable; + + // Whitelist subclasses that access the destructor of BindStateBase. + template + friend struct BindState; + friend struct ::base::FakeBindState; + + bool IsCancelled() const { return is_cancelled_(this); } + + // In C++, it is safe to cast function pointers to function pointers of + // another type. It is not okay to use void*. We create a InvokeFuncStorage + // that that can store our function pointer, and then cast it back to + // the original type on usage. + InvokeFuncStorage polymorphic_invoke_; + + // Pointer to a function that will properly destroy |this|. + void (*destructor_)(const BindStateBase*); + bool (*is_cancelled_)(const BindStateBase*); + + DISALLOW_COPY_AND_ASSIGN(BindStateBase); +}; + +// Holds the Callback methods that don't require specialization to reduce +// template bloat. +// CallbackBase is a direct base class of MoveOnly callbacks, and +// CallbackBase uses CallbackBase for its implementation. +class CallbackBase { + public: + CallbackBase(CallbackBase&& c) noexcept; + CallbackBase& operator=(CallbackBase&& c) noexcept; + + explicit CallbackBase(const CallbackBaseCopyable& c); + CallbackBase& operator=(const CallbackBaseCopyable& c); + + explicit CallbackBase(CallbackBaseCopyable&& c) noexcept; + CallbackBase& operator=(CallbackBaseCopyable&& c) noexcept; + + // Returns true if Callback is null (doesn't refer to anything). + bool is_null() const { return !bind_state_; } + explicit operator bool() const { return !is_null(); } + + // Returns true if the callback invocation will be nop due to an cancellation. + // It's invalid to call this on uninitialized callback. + bool IsCancelled() const; + + // Returns the Callback into an uninitialized state. + void Reset(); + + protected: + using InvokeFuncStorage = BindStateBase::InvokeFuncStorage; + + // Returns true if this callback equals |other|. |other| may be null. + bool EqualsInternal(const CallbackBase& other) const; + + constexpr inline CallbackBase(); + + // Allow initializing of |bind_state_| via the constructor to avoid default + // initialization of the scoped_refptr. + explicit CallbackBase(BindStateBase* bind_state); + + InvokeFuncStorage polymorphic_invoke() const { + return bind_state_->polymorphic_invoke_; + } + + // Force the destructor to be instantiated inside this translation unit so + // that our subclasses will not get inlined versions. Avoids more template + // bloat. + ~CallbackBase(); + + scoped_refptr bind_state_; +}; + +constexpr CallbackBase::CallbackBase() = default; + +// CallbackBase is a direct base class of Copyable Callbacks. +class CallbackBaseCopyable : public CallbackBase { + public: + CallbackBaseCopyable(const CallbackBaseCopyable& c); + CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept; + CallbackBaseCopyable& operator=(const CallbackBaseCopyable& c); + CallbackBaseCopyable& operator=(CallbackBaseCopyable&& c) noexcept; + + protected: + constexpr CallbackBaseCopyable() = default; + explicit CallbackBaseCopyable(BindStateBase* bind_state) + : CallbackBase(bind_state) {} + ~CallbackBaseCopyable() = default; +}; + +} // namespace internal +} // namespace base + +#endif // BASE_CALLBACK_INTERNAL_H_ diff --git a/src/3rdparty/gn/base/command_line.cc b/src/3rdparty/gn/base/command_line.cc new file mode 100644 index 00000000000..792d322fe1e --- /dev/null +++ b/src/3rdparty/gn/base/command_line.cc @@ -0,0 +1,486 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/command_line.h" + +#include +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/stl_util.h" +#include "base/strings/string_split.h" +#include "base/strings/string_tokenizer.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "util/build_config.h" + +#if defined(OS_WIN) +#include + +#include +#endif + +namespace base { + +CommandLine* CommandLine::current_process_commandline_ = nullptr; + +namespace { + +const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); +const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); + +// Since we use a lazy match, make sure that longer versions (like "--") are +// listed before shorter versions (like "-") of similar prefixes. +#if defined(OS_WIN) +// By putting slash last, we can control whether it is treaded as a switch +// value by changing the value of switch_prefix_count to be one less than +// the array size. +const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +// Unixes don't use slash as a switch. +const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; +#endif +size_t switch_prefix_count = arraysize(kSwitchPrefixes); + +size_t GetSwitchPrefixLength(const CommandLine::StringType& string) { + for (size_t i = 0; i < switch_prefix_count; ++i) { + CommandLine::StringType prefix(kSwitchPrefixes[i]); + if (string.compare(0, prefix.length(), prefix) == 0) + return prefix.length(); + } + return 0; +} + +// Fills in |switch_string| and |switch_value| if |string| is a switch. +// This will preserve the input switch prefix in the output |switch_string|. +bool IsSwitch(const CommandLine::StringType& string, + CommandLine::StringType* switch_string, + CommandLine::StringType* switch_value) { + switch_string->clear(); + switch_value->clear(); + size_t prefix_length = GetSwitchPrefixLength(string); + if (prefix_length == 0 || prefix_length == string.length()) + return false; + + const size_t equals_position = string.find(kSwitchValueSeparator); + *switch_string = string.substr(0, equals_position); + if (equals_position != CommandLine::StringType::npos) + *switch_value = string.substr(equals_position + 1); + return true; +} + +// Append switches and arguments, keeping switches before arguments +// if handle_switches is true. +void AppendSwitchesAndArguments(CommandLine* command_line, + const CommandLine::StringVector& argv, + bool handle_switches) { + bool parse_switches = handle_switches; + for (size_t i = 1; i < argv.size(); ++i) { + CommandLine::StringType arg = argv[i]; +#if defined(OS_WIN) + TrimWhitespace(arg, TRIM_ALL, &arg); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + TrimWhitespaceASCII(arg, TRIM_ALL, &arg); +#endif + + CommandLine::StringType switch_string; + CommandLine::StringType switch_value; + parse_switches &= (arg != kSwitchTerminator); + if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { +#if defined(OS_WIN) + command_line->AppendSwitchNative(UTF16ToASCII(switch_string), + switch_value); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + command_line->AppendSwitchNative(switch_string, switch_value); +#else +#error Unsupported platform +#endif + } else { + command_line->AppendArgNative(arg); + } + } +} + +#if defined(OS_WIN) +// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. +string16 QuoteForCommandLineToArgvW(const string16& arg, + bool quote_placeholders) { + // We follow the quoting rules of CommandLineToArgvW. + // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + string16 quotable_chars(L" \\\""); + // We may also be required to quote '%', which is commonly used in a command + // line as a placeholder. (It may be substituted for a string with spaces.) + if (quote_placeholders) + quotable_chars.push_back(L'%'); + if (arg.find_first_of(quotable_chars) == string16::npos) { + // No quoting necessary. + return arg; + } + + string16 out; + out.push_back(L'"'); + for (size_t i = 0; i < arg.size(); ++i) { + if (arg[i] == '\\') { + // Find the extent of this run of backslashes. + size_t start = i, end = start + 1; + for (; end < arg.size() && arg[end] == '\\'; ++end) { + } + size_t backslash_count = end - start; + + // Backslashes are escapes only if the run is followed by a double quote. + // Since we also will end the string with a double quote, we escape for + // either a double quote or the end of the string. + if (end == arg.size() || arg[end] == '"') { + // To quote, we need to output 2x as many backslashes. + backslash_count *= 2; + } + for (size_t j = 0; j < backslash_count; ++j) + out.push_back('\\'); + + // Advance i to one before the end to balance i++ in loop. + i = end - 1; + } else if (arg[i] == '"') { + out.push_back('\\'); + out.push_back('"'); + } else { + out.push_back(arg[i]); + } + } + out.push_back('"'); + + return out; +} +#endif + +} // namespace + +CommandLine::CommandLine(NoProgram no_program) + : argv_(1), begin_args_(1), parse_switches_(true) {} + +CommandLine::CommandLine(const FilePath& program) + : argv_(1), begin_args_(1), parse_switches_(true) { + SetProgram(program); +} + +CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) + : argv_(1), begin_args_(1), parse_switches_(true) { + InitFromArgv(argc, argv); +} + +CommandLine::CommandLine(const StringVector& argv) + : argv_(1), begin_args_(1), parse_switches_(true) { + InitFromArgv(argv); +} + +CommandLine::CommandLine(const CommandLine& other) = default; + +CommandLine& CommandLine::operator=(const CommandLine& other) = default; + +CommandLine::~CommandLine() = default; + +#if defined(OS_WIN) +// static +void CommandLine::set_slash_is_not_a_switch() { + // The last switch prefix should be slash, so adjust the size to skip it. + DCHECK_EQ(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/"), 0); + switch_prefix_count = arraysize(kSwitchPrefixes) - 1; +} + +// static +void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) { + DCHECK(!current_process_commandline_); + current_process_commandline_ = new CommandLine(NO_PROGRAM); + // On Windows we need to convert the command line arguments to string16. + base::CommandLine::StringVector argv_vector; + for (int i = 0; i < argc; ++i) + argv_vector.push_back(UTF8ToUTF16(argv[i])); + current_process_commandline_->InitFromArgv(argv_vector); +} +#endif + +// static +bool CommandLine::Init(int argc, const char* const* argv) { + if (current_process_commandline_) { + // If this is intentional, Reset() must be called first. If we are using + // the shared build mode, we have to share a single object across multiple + // shared libraries. + return false; + } + + current_process_commandline_ = new CommandLine(NO_PROGRAM); +#if defined(OS_WIN) + current_process_commandline_->ParseFromString(::GetCommandLineW()); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + current_process_commandline_->InitFromArgv(argc, argv); +#else +#error Unsupported platform +#endif + + return true; +} + +// static +void CommandLine::Reset() { + DCHECK(current_process_commandline_); + delete current_process_commandline_; + current_process_commandline_ = nullptr; +} + +// static +CommandLine* CommandLine::ForCurrentProcess() { + DCHECK(current_process_commandline_); + return current_process_commandline_; +} + +// static +bool CommandLine::InitializedForCurrentProcess() { + return !!current_process_commandline_; +} + +#if defined(OS_WIN) +// static +CommandLine CommandLine::FromString(const string16& command_line) { + CommandLine cmd(NO_PROGRAM); + cmd.ParseFromString(command_line); + return cmd; +} +#endif + +void CommandLine::InitFromArgv(int argc, + const CommandLine::CharType* const* argv) { + StringVector new_argv; + for (int i = 0; i < argc; ++i) + new_argv.push_back(argv[i]); + InitFromArgv(new_argv); +} + +void CommandLine::InitFromArgv(const StringVector& argv) { + argv_ = StringVector(1); + switches_.clear(); + begin_args_ = 1; + SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); + AppendSwitchesAndArguments(this, argv, parse_switches_); +} + +FilePath CommandLine::GetProgram() const { + return FilePath(argv_[0]); +} + +void CommandLine::SetProgram(const FilePath& program) { +#if defined(OS_WIN) + TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]); +#else +#error Unsupported platform +#endif +} + +bool CommandLine::HasSwitch(const base::StringPiece& switch_string) const { + DCHECK_EQ(ToLowerASCII(switch_string), switch_string); + return ContainsKey(switches_, switch_string); +} + +bool CommandLine::HasSwitch(const char switch_constant[]) const { + return HasSwitch(base::StringPiece(switch_constant)); +} + +std::string CommandLine::GetSwitchValueASCII( + const base::StringPiece& switch_string) const { + StringType value = GetSwitchValueNative(switch_string); + if (!IsStringASCII(value)) { + DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; + return std::string(); + } +#if defined(OS_WIN) + return UTF16ToASCII(value); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return value; +#endif +} + +FilePath CommandLine::GetSwitchValuePath( + const base::StringPiece& switch_string) const { + return FilePath(GetSwitchValueNative(switch_string)); +} + +CommandLine::StringType CommandLine::GetSwitchValueNative( + const base::StringPiece& switch_string) const { + DCHECK_EQ(ToLowerASCII(switch_string), switch_string); + auto result = switches_.find(switch_string); + return result == switches_.end() ? StringType() : result->second; +} + +void CommandLine::AppendSwitch(const std::string& switch_string) { + AppendSwitchNative(switch_string, StringType()); +} + +void CommandLine::AppendSwitchPath(const std::string& switch_string, + const FilePath& path) { + AppendSwitchNative(switch_string, path.value()); +} + +void CommandLine::AppendSwitchNative(const std::string& switch_string, + const CommandLine::StringType& value) { +#if defined(OS_WIN) + const std::string switch_key = ToLowerASCII(switch_string); + StringType combined_switch_string(ASCIIToUTF16(switch_key)); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + const std::string& switch_key = switch_string; + StringType combined_switch_string(switch_key); +#endif + size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); + auto insertion = + switches_.insert(make_pair(switch_key.substr(prefix_length), value)); + if (!insertion.second) + insertion.first->second = value; + // Preserve existing switch prefixes in |argv_|; only append one if necessary. + if (prefix_length == 0) + combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; + if (!value.empty()) + combined_switch_string += kSwitchValueSeparator + value; + // Append the switch and update the switches/arguments divider |begin_args_|. + argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); +} + +void CommandLine::AppendSwitchASCII(const std::string& switch_string, + const std::string& value_string) { +#if defined(OS_WIN) + AppendSwitchNative(switch_string, ASCIIToUTF16(value_string)); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + AppendSwitchNative(switch_string, value_string); +#else +#error Unsupported platform +#endif +} + +void CommandLine::CopySwitchesFrom(const CommandLine& source, + const char* const switches[], + size_t count) { + for (size_t i = 0; i < count; ++i) { + if (source.HasSwitch(switches[i])) + AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i])); + } +} + +CommandLine::StringVector CommandLine::GetArgs() const { + // Gather all arguments after the last switch (may include kSwitchTerminator). + StringVector args(argv_.begin() + begin_args_, argv_.end()); + // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) + StringVector::iterator switch_terminator = + std::find(args.begin(), args.end(), kSwitchTerminator); + if (switch_terminator != args.end()) + args.erase(switch_terminator); + return args; +} + +void CommandLine::AppendArg(const std::string& value) { +#if defined(OS_WIN) + DCHECK(IsStringUTF8(value)); + AppendArgNative(UTF8ToWide(value)); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + AppendArgNative(value); +#else +#error Unsupported platform +#endif +} + +void CommandLine::AppendArgPath(const FilePath& path) { + AppendArgNative(path.value()); +} + +void CommandLine::AppendArgNative(const CommandLine::StringType& value) { + argv_.push_back(value); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + if (include_program) + SetProgram(other.GetProgram()); + AppendSwitchesAndArguments(this, other.argv(), parse_switches_); +} + +void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { + if (wrapper.empty()) + return; + // Split the wrapper command based on whitespace (with quoting). + using CommandLineTokenizer = + StringTokenizerT; + CommandLineTokenizer tokenizer(wrapper, FILE_PATH_LITERAL(" ")); + tokenizer.set_quote_chars(FILE_PATH_LITERAL("'\"")); + std::vector wrapper_argv; + while (tokenizer.GetNext()) + wrapper_argv.emplace_back(tokenizer.token()); + + // Prepend the wrapper and update the switches/arguments |begin_args_|. + argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end()); + begin_args_ += wrapper_argv.size(); +} + +#if defined(OS_WIN) +void CommandLine::ParseFromString(const string16& command_line) { + string16 command_line_string; + TrimWhitespace(command_line, TRIM_ALL, &command_line_string); + if (command_line_string.empty()) + return; + + int num_args = 0; + wchar_t** args = NULL; + args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); + + DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " + << UTF16ToUTF8(command_line); + InitFromArgv(num_args, args); + LocalFree(args); +} +#endif + +CommandLine::StringType CommandLine::GetCommandLineStringInternal( + bool quote_placeholders) const { + StringType string(argv_[0]); +#if defined(OS_WIN) + string = QuoteForCommandLineToArgvW(string, quote_placeholders); +#endif + StringType params(GetArgumentsStringInternal(quote_placeholders)); + if (!params.empty()) { + string.append(StringType(FILE_PATH_LITERAL(" "))); + string.append(params); + } + return string; +} + +CommandLine::StringType CommandLine::GetArgumentsStringInternal( + bool quote_placeholders) const { + StringType params; + // Append switches and arguments. + bool parse_switches = parse_switches_; + for (size_t i = 1; i < argv_.size(); ++i) { + StringType arg = argv_[i]; + StringType switch_string; + StringType switch_value; + parse_switches &= arg != kSwitchTerminator; + if (i > 1) + params.append(StringType(FILE_PATH_LITERAL(" "))); + if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { + params.append(switch_string); + if (!switch_value.empty()) { +#if defined(OS_WIN) + switch_value = + QuoteForCommandLineToArgvW(switch_value, quote_placeholders); +#endif + params.append(kSwitchValueSeparator + switch_value); + } + } else { +#if defined(OS_WIN) + arg = QuoteForCommandLineToArgvW(arg, quote_placeholders); +#endif + params.append(arg); + } + } + return params; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/command_line.h b/src/3rdparty/gn/base/command_line.h new file mode 100644 index 00000000000..01614576832 --- /dev/null +++ b/src/3rdparty/gn/base/command_line.h @@ -0,0 +1,255 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This class works with command lines: building and parsing. +// Arguments with prefixes ('--', '-', and on Windows, '/') are switches. +// Switches will precede all other arguments without switch prefixes. +// Switches can optionally have values, delimited by '=', e.g., "-switch=value". +// An argument of "--" will terminate switch parsing during initialization, +// interpreting subsequent tokens as non-switch arguments, regardless of prefix. + +// There is a singleton read-only CommandLine that represents the command line +// that the current process was started with. It must be initialized in main(). + +#ifndef BASE_COMMAND_LINE_H_ +#define BASE_COMMAND_LINE_H_ + +#include +#include +#include +#include + +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "util/build_config.h" + +namespace base { + +class FilePath; + +class CommandLine { + public: +#if defined(OS_WIN) + // The native command line string type. + using StringType = string16; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + using StringType = std::string; +#endif + + using CharType = StringType::value_type; + using StringVector = std::vector; + using SwitchMap = std::map>; + + // A constructor for CommandLines that only carry switches and arguments. + enum NoProgram { NO_PROGRAM }; + explicit CommandLine(NoProgram no_program); + + // Construct a new command line with |program| as argv[0]. + explicit CommandLine(const FilePath& program); + + // Construct a new command line from an argument list. + CommandLine(int argc, const CharType* const* argv); + explicit CommandLine(const StringVector& argv); + + // Override copy and assign to ensure |switches_by_stringpiece_| is valid. + CommandLine(const CommandLine& other); + CommandLine& operator=(const CommandLine& other); + + ~CommandLine(); + +#if defined(OS_WIN) + // By default this class will treat command-line arguments beginning with + // slashes as switches on Windows, but not other platforms. + // + // If this behavior is inappropriate for your application, you can call this + // function BEFORE initializing the current process' global command line + // object and the behavior will be the same as Posix systems (only hyphens + // begin switches, everything else will be an arg). + static void set_slash_is_not_a_switch(); + + // Normally when the CommandLine singleton is initialized it gets the command + // line via the GetCommandLineW API and then uses the shell32 API + // CommandLineToArgvW to parse the command line and convert it back to + // argc and argv. Tests who don't want this dependency on shell32 and need + // to honor the arguments passed in should use this function. + static void InitUsingArgvForTesting(int argc, const char* const* argv); +#endif + + // Initialize the current process CommandLine singleton. On Windows, ignores + // its arguments (we instead parse GetCommandLineW() directly) because we + // don't trust the CRT's parsing of the command line, but it still must be + // called to set up the command line. Returns false if initialization has + // already occurred, and true otherwise. Only the caller receiving a 'true' + // return value should take responsibility for calling Reset. + static bool Init(int argc, const char* const* argv); + + // Destroys the current process CommandLine singleton. This is necessary if + // you want to reset the base library to its initial state (for example, in an + // outer library that needs to be able to terminate, and be re-initialized). + // If Init is called only once, as in main(), Reset() is not necessary. + // Do not call this in tests. Use base::test::ScopedCommandLine instead. + static void Reset(); + + // Get the singleton CommandLine representing the current process's + // command line. Note: returned value is mutable, but not thread safe; + // only mutate if you know what you're doing! + static CommandLine* ForCurrentProcess(); + + // Returns true if the CommandLine has been initialized for the given process. + static bool InitializedForCurrentProcess(); + +#if defined(OS_WIN) + static CommandLine FromString(const string16& command_line); +#endif + + // Initialize from an argv vector. + void InitFromArgv(int argc, const CharType* const* argv); + void InitFromArgv(const StringVector& argv); + + // Constructs and returns the represented command line string. + // CAUTION! This should be avoided on POSIX because quoting behavior is + // unclear. + StringType GetCommandLineString() const { + return GetCommandLineStringInternal(false); + } + +#if defined(OS_WIN) + // Constructs and returns the represented command line string. Assumes the + // command line contains placeholders (eg, %1) and quotes any program or + // argument with a '%' in it. This should be avoided unless the placeholder is + // required by an external interface (eg, the Windows registry), because it is + // not generally safe to replace it with an arbitrary string. If possible, + // placeholders should be replaced *before* converting the command line to a + // string. + StringType GetCommandLineStringWithPlaceholders() const { + return GetCommandLineStringInternal(true); + } +#endif + + // Constructs and returns the represented arguments string. + // CAUTION! This should be avoided on POSIX because quoting behavior is + // unclear. + StringType GetArgumentsString() const { + return GetArgumentsStringInternal(false); + } + +#if defined(OS_WIN) + // Constructs and returns the represented arguments string. Assumes the + // command line contains placeholders (eg, %1) and quotes any argument with a + // '%' in it. This should be avoided unless the placeholder is required by an + // external interface (eg, the Windows registry), because it is not generally + // safe to replace it with an arbitrary string. If possible, placeholders + // should be replaced *before* converting the arguments to a string. + StringType GetArgumentsStringWithPlaceholders() const { + return GetArgumentsStringInternal(true); + } +#endif + + // Returns the original command line string as a vector of strings. + const StringVector& argv() const { return argv_; } + + // Get and Set the program part of the command line string (the first item). + FilePath GetProgram() const; + void SetProgram(const FilePath& program); + + // Enables/disables the parsing of switches for future argument appending. + // True by default, but can be set to false to ensure that no re-ordering + // is done. + void SetParseSwitches(bool parse_switches) { + parse_switches_ = parse_switches; + } + + // Returns true if this command line contains the given switch. + // Switch names must be lowercase. + // The second override provides an optimized version to avoid inlining codegen + // at every callsite to find the length of the constant and construct a + // StringPiece. + bool HasSwitch(const StringPiece& switch_string) const; + bool HasSwitch(const char switch_constant[]) const; + + // Returns the value associated with the given switch. If the switch has no + // value or isn't present, this method returns the empty string. + // Switch names must be lowercase. + std::string GetSwitchValueASCII(const StringPiece& switch_string) const; + FilePath GetSwitchValuePath(const StringPiece& switch_string) const; + StringType GetSwitchValueNative(const StringPiece& switch_string) const; + + // Get a copy of all switches, along with their values. + const SwitchMap& GetSwitches() const { return switches_; } + + // Append a switch [with optional value] to the command line. + // Note: Switches will precede arguments regardless of appending order. + void AppendSwitch(const std::string& switch_string); + void AppendSwitchPath(const std::string& switch_string, const FilePath& path); + void AppendSwitchNative(const std::string& switch_string, + const StringType& value); + void AppendSwitchASCII(const std::string& switch_string, + const std::string& value); + + // Copy a set of switches (and any values) from another command line. + // Commonly used when launching a subprocess. + void CopySwitchesFrom(const CommandLine& source, + const char* const switches[], + size_t count); + + // Get the remaining arguments to the command. + StringVector GetArgs() const; + + // Append an argument to the command line. Note that the argument is quoted + // properly such that it is interpreted as one argument to the target command. + // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8. + // Note: Switches will precede arguments regardless of appending order. + void AppendArg(const std::string& value); + void AppendArgPath(const FilePath& value); + void AppendArgNative(const StringType& value); + + // Append the switches and arguments from another command line to this one. + // If |include_program| is true, include |other|'s program as well. + void AppendArguments(const CommandLine& other, bool include_program); + + // Insert a command before the current command. + // Common for debuggers, like "gdb --args". + void PrependWrapper(const StringType& wrapper); + +#if defined(OS_WIN) + // Initialize by parsing the given command line string. + // The program name is assumed to be the first item in the string. + void ParseFromString(const string16& command_line); +#endif + + private: + // Disallow default constructor; a program name must be explicitly specified. + CommandLine() = delete; + // Allow the copy constructor. A common pattern is to copy of the current + // process's command line and then add some flags to it. For example: + // CommandLine cl(*CommandLine::ForCurrentProcess()); + // cl.AppendSwitch(...); + + // Internal version of GetCommandLineString. If |quote_placeholders| is true, + // also quotes parts with '%' in them. + StringType GetCommandLineStringInternal(bool quote_placeholders) const; + + // Internal version of GetArgumentsString. If |quote_placeholders| is true, + // also quotes parts with '%' in them. + StringType GetArgumentsStringInternal(bool quote_placeholders) const; + + // The singleton CommandLine representing the current process's command line. + static CommandLine* current_process_commandline_; + + // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* } + StringVector argv_; + + // Parsed-out switch keys and values. + SwitchMap switches_; + + // The index after the program and switches, any arguments start here. + size_t begin_args_; + + // Whether or not to parse arguments that look like switches as switches. + bool parse_switches_; +}; + +} // namespace base + +#endif // BASE_COMMAND_LINE_H_ diff --git a/src/3rdparty/gn/base/compiler_specific.h b/src/3rdparty/gn/base/compiler_specific.h new file mode 100644 index 00000000000..42241976483 --- /dev/null +++ b/src/3rdparty/gn/base/compiler_specific.h @@ -0,0 +1,231 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_COMPILER_SPECIFIC_H_ +#define BASE_COMPILER_SPECIFIC_H_ + +#include "util/build_config.h" + +#if defined(COMPILER_MSVC) + +// For _Printf_format_string_. +#include + +// Macros for suppressing and disabling warnings on MSVC. +// +// Warning numbers are enumerated at: +// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx +// +// The warning pragma: +// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx +// +// Using __pragma instead of #pragma inside macros: +// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx + +// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and +// for the next line of the source file. +#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress : n)) + +// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. +// The warning remains disabled until popped by MSVC_POP_WARNING. +#define MSVC_PUSH_DISABLE_WARNING(n) \ + __pragma(warning(push)) __pragma(warning(disable : n)) + +// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level +// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all +// warnings. +#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n)) + +// Pop effects of innermost MSVC_PUSH_* macro. +#define MSVC_POP_WARNING() __pragma(warning(pop)) + +#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off)) +#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on)) + +#else // Not MSVC + +#define _Printf_format_string_ +#define MSVC_SUPPRESS_WARNING(n) +#define MSVC_PUSH_DISABLE_WARNING(n) +#define MSVC_PUSH_WARNING_LEVEL(n) +#define MSVC_POP_WARNING() +#define MSVC_DISABLE_OPTIMIZE() +#define MSVC_ENABLE_OPTIMIZE() + +#endif // COMPILER_MSVC + +// Annotate a variable indicating it's ok if the variable is not used. +// (Typically used to silence a compiler warning when the assignment +// is important for some other reason.) +// Use like: +// int x = ...; +// ALLOW_UNUSED_LOCAL(x); +#define ALLOW_UNUSED_LOCAL(x) (void)x + +// Annotate a typedef or function indicating it's ok if it's not used. +// Use like: +// typedef Foo Bar ALLOW_UNUSED_TYPE; +#if defined(COMPILER_GCC) || defined(__clang__) +#define ALLOW_UNUSED_TYPE __attribute__((unused)) +#else +#define ALLOW_UNUSED_TYPE +#endif + +// Annotate a function indicating it should not be inlined. +// Use like: +// NOINLINE void DoStuff() { ... } +#if defined(COMPILER_GCC) +#define NOINLINE __attribute__((noinline)) +#elif defined(COMPILER_MSVC) +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE +#endif + +#if COMPILER_GCC && defined(NDEBUG) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif COMPILER_MSVC && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif + +// Specify memory alignment for structs, classes, etc. +// Use like: +// class ALIGNAS(16) MyClass { ... } +// ALIGNAS(16) int array[4]; +// +// In most places you can use the C++11 keyword "alignas", which is preferred. +// +// But compilers have trouble mixing __attribute__((...)) syntax with +// alignas(...) syntax. +// +// Doesn't work in clang or gcc: +// struct alignas(16) __attribute__((packed)) S { char c; }; +// Works in clang but not gcc: +// struct __attribute__((packed)) alignas(16) S2 { char c; }; +// Works in clang and gcc: +// struct alignas(16) S3 { char c; } __attribute__((packed)); +// +// There are also some attributes that must be specified *before* a class +// definition: visibility (used for exporting functions/classes) is one of +// these attributes. This means that it is not possible to use alignas() with a +// class that is marked as exported. +#if defined(COMPILER_MSVC) +#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) +#elif defined(COMPILER_GCC) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#endif + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in base/macros.h. +#undef WARN_UNUSED_RESULT +#if defined(COMPILER_GCC) || defined(__clang__) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif + +// Tell the compiler a function is using a printf-style format string. +// |format_param| is the one-based index of the format string parameter; +// |dots_param| is the one-based index of the "..." parameter. +// For v*printf functions (which take a va_list), pass 0 for dots_param. +// (This is undocumented but matches what the system C headers do.) +#if defined(COMPILER_GCC) || defined(__clang__) +#define PRINTF_FORMAT(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define PRINTF_FORMAT(format_param, dots_param) +#endif + +// WPRINTF_FORMAT is the same, but for wide format strings. +// This doesn't appear to yet be implemented in any compiler. +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 . +#define WPRINTF_FORMAT(format_param, dots_param) +// If available, it would look like: +// __attribute__((format(wprintf, format_param, dots_param))) + +// Sanitizers annotations. +#if defined(__has_attribute) +#if __has_attribute(no_sanitize) +#define NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#endif +#if !defined(NO_SANITIZE) +#define NO_SANITIZE(what) +#endif + +// MemorySanitizer annotations. +#if defined(MEMORY_SANITIZER) && !defined(OS_NACL) +#include + +// Mark a memory region fully initialized. +// Use this to annotate code that deliberately reads uninitialized data, for +// example a GC scavenging root set pointers from the stack. +#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size) + +// Check a memory region for initializedness, as if it was being used here. +// If any bits are uninitialized, crash with an MSan report. +// Use this to sanitize data which MSan won't be able to track, e.g. before +// passing data to another process via shared memory. +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \ + __msan_check_mem_is_initialized(p, size) +#else // MEMORY_SANITIZER +#define MSAN_UNPOISON(p, size) +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) +#endif // MEMORY_SANITIZER + +// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons. +#if !defined(DISABLE_CFI_PERF) +#if defined(__clang__) && defined(OFFICIAL_BUILD) +#define DISABLE_CFI_PERF __attribute__((no_sanitize("cfi"))) +#else +#define DISABLE_CFI_PERF +#endif +#endif + +// Macro useful for writing cross-platform function pointers. +#if !defined(CDECL) +#if defined(OS_WIN) +#define CDECL __cdecl +#else // defined(OS_WIN) +#define CDECL +#endif // defined(OS_WIN) +#endif // !defined(CDECL) + +// Macro for hinting that an expression is likely to be false. +#if !defined(UNLIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define UNLIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(UNLIKELY) + +#if !defined(LIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define LIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(LIKELY) + +// Compiler feature-detection. +// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension +#if defined(__has_feature) +#define HAS_FEATURE(FEATURE) __has_feature(FEATURE) +#else +#define HAS_FEATURE(FEATURE) 0 +#endif + +// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional. +#if defined(__clang__) +#define FALLTHROUGH [[clang::fallthrough]] +#else +#define FALLTHROUGH +#endif + +#endif // BASE_COMPILER_SPECIFIC_H_ diff --git a/src/3rdparty/gn/base/containers/circular_deque.h b/src/3rdparty/gn/base/containers/circular_deque.h new file mode 100644 index 00000000000..bf42a958448 --- /dev/null +++ b/src/3rdparty/gn/base/containers/circular_deque.h @@ -0,0 +1,1111 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_CIRCULAR_DEQUE_H_ +#define BASE_CONTAINERS_CIRCULAR_DEQUE_H_ + +#include +#include +#include +#include +#include + +#include "base/containers/vector_buffer.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/template_util.h" + +// base::circular_deque is similar to std::deque. Unlike std::deque, the +// storage is provided in a flat circular buffer conceptually similar to a +// vector. The beginning and end will wrap around as necessary so that +// pushes and pops will be constant time as long as a capacity expansion is +// not required. +// +// The API should be identical to std::deque with the following differences: +// +// - ITERATORS ARE NOT STABLE. Mutating the container will invalidate all +// iterators. +// +// - Insertions may resize the vector and so are not constant time (std::deque +// guarantees constant time for insertions at the ends). +// +// - Container-wide comparisons are not implemented. If you want to compare +// two containers, use an algorithm so the expensive iteration is explicit. +// +// If you want a similar container with only a queue API, use base::queue in +// base/containers/queue.h. +// +// Constructors: +// circular_deque(); +// circular_deque(size_t count); +// circular_deque(size_t count, const T& value); +// circular_deque(InputIterator first, InputIterator last); +// circular_deque(const circular_deque&); +// circular_deque(circular_deque&&); +// circular_deque(std::initializer_list); +// +// Assignment functions: +// circular_deque& operator=(const circular_deque&); +// circular_deque& operator=(circular_deque&&); +// circular_deque& operator=(std::initializer_list); +// void assign(size_t count, const T& value); +// void assign(InputIterator first, InputIterator last); +// void assign(std::initializer_list value); +// +// Random accessors: +// T& at(size_t); +// const T& at(size_t) const; +// T& operator[](size_t); +// const T& operator[](size_t) const; +// +// End accessors: +// T& front(); +// const T& front() const; +// T& back(); +// const T& back() const; +// +// Iterator functions: +// iterator begin(); +// const_iterator begin() const; +// const_iterator cbegin() const; +// iterator end(); +// const_iterator end() const; +// const_iterator cend() const; +// reverse_iterator rbegin(); +// const_reverse_iterator rbegin() const; +// const_reverse_iterator crbegin() const; +// reverse_iterator rend(); +// const_reverse_iterator rend() const; +// const_reverse_iterator crend() const; +// +// Memory management: +// void reserve(size_t); // SEE IMPLEMENTATION FOR SOME GOTCHAS. +// size_t capacity() const; +// void shrink_to_fit(); +// +// Size management: +// void clear(); +// bool empty() const; +// size_t size() const; +// void resize(size_t); +// void resize(size_t count, const T& value); +// +// Positional insert and erase: +// void insert(const_iterator pos, size_type count, const T& value); +// void insert(const_iterator pos, +// InputIterator first, InputIterator last); +// iterator insert(const_iterator pos, const T& value); +// iterator insert(const_iterator pos, T&& value); +// iterator emplace(const_iterator pos, Args&&... args); +// iterator erase(const_iterator pos); +// iterator erase(const_iterator first, const_iterator last); +// +// End insert and erase: +// void push_front(const T&); +// void push_front(T&&); +// void push_back(const T&); +// void push_back(T&&); +// T& emplace_front(Args&&...); +// T& emplace_back(Args&&...); +// void pop_front(); +// void pop_back(); +// +// General: +// void swap(circular_deque&); + +namespace base { + +template +class circular_deque; + +namespace internal { + +// Start allocating nonempty buffers with this many entries. This is the +// external capacity so the internal buffer will be one larger (= 4) which is +// more even for the allocator. See the descriptions of internal vs. external +// capacity on the comment above the buffer_ variable below. +constexpr size_t kCircularBufferInitialCapacity = 3; + +template +class circular_deque_const_iterator { + public: + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; + using iterator_category = std::random_access_iterator_tag; + + circular_deque_const_iterator() : parent_deque_(nullptr), index_(0) { +#if DCHECK_IS_ON() + created_generation_ = 0; +#endif // DCHECK_IS_ON() + } + + // Dereferencing. + const T& operator*() const { + CheckUnstableUsage(); + parent_deque_->CheckValidIndex(index_); + return parent_deque_->buffer_[index_]; + } + const T* operator->() const { + CheckUnstableUsage(); + parent_deque_->CheckValidIndex(index_); + return &parent_deque_->buffer_[index_]; + } + const value_type& operator[](difference_type i) const { return *(*this + i); } + + // Increment and decrement. + circular_deque_const_iterator& operator++() { + Increment(); + return *this; + } + circular_deque_const_iterator operator++(int) { + circular_deque_const_iterator ret = *this; + Increment(); + return ret; + } + circular_deque_const_iterator& operator--() { + Decrement(); + return *this; + } + circular_deque_const_iterator operator--(int) { + circular_deque_const_iterator ret = *this; + Decrement(); + return ret; + } + + // Random access mutation. + friend circular_deque_const_iterator operator+( + const circular_deque_const_iterator& iter, + difference_type offset) { + circular_deque_const_iterator ret = iter; + ret.Add(offset); + return ret; + } + circular_deque_const_iterator& operator+=(difference_type offset) { + Add(offset); + return *this; + } + friend circular_deque_const_iterator operator-( + const circular_deque_const_iterator& iter, + difference_type offset) { + circular_deque_const_iterator ret = iter; + ret.Add(-offset); + return ret; + } + circular_deque_const_iterator& operator-=(difference_type offset) { + Add(-offset); + return *this; + } + + friend std::ptrdiff_t operator-(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.OffsetFromBegin() - rhs.OffsetFromBegin(); + } + + // Comparisons. + friend bool operator==(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.index_ == rhs.index_; + } + friend bool operator!=(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + return !(lhs == rhs); + } + friend bool operator<(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.OffsetFromBegin() < rhs.OffsetFromBegin(); + } + friend bool operator<=(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + return !(lhs > rhs); + } + friend bool operator>(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.OffsetFromBegin() > rhs.OffsetFromBegin(); + } + friend bool operator>=(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + return !(lhs < rhs); + } + + protected: + friend class circular_deque; + + circular_deque_const_iterator(const circular_deque* parent, size_t index) + : parent_deque_(parent), index_(index) { +#if DCHECK_IS_ON() + created_generation_ = parent->generation_; +#endif // DCHECK_IS_ON() + } + + // Returns the offset from the beginning index of the buffer to the current + // item. + size_t OffsetFromBegin() const { + if (index_ >= parent_deque_->begin_) + return index_ - parent_deque_->begin_; // On the same side as begin. + return parent_deque_->buffer_.capacity() - parent_deque_->begin_ + index_; + } + + // Most uses will be ++ and -- so use a simplified implementation. + void Increment() { + CheckUnstableUsage(); + parent_deque_->CheckValidIndex(index_); + index_++; + if (index_ == parent_deque_->buffer_.capacity()) + index_ = 0; + } + void Decrement() { + CheckUnstableUsage(); + parent_deque_->CheckValidIndexOrEnd(index_); + if (index_ == 0) + index_ = parent_deque_->buffer_.capacity() - 1; + else + index_--; + } + void Add(difference_type delta) { + CheckUnstableUsage(); +#if DCHECK_IS_ON() + if (delta <= 0) + parent_deque_->CheckValidIndexOrEnd(index_); + else + parent_deque_->CheckValidIndex(index_); +#endif + // It should be valid to add 0 to any iterator, even if the container is + // empty and the iterator points to end(). The modulo below will divide + // by 0 if the buffer capacity is empty, so it's important to check for + // this case explicitly. + if (delta == 0) + return; + + difference_type new_offset = OffsetFromBegin() + delta; + DCHECK(new_offset >= 0 && + new_offset <= static_cast(parent_deque_->size())); + index_ = (new_offset + parent_deque_->begin_) % + parent_deque_->buffer_.capacity(); + } + +#if DCHECK_IS_ON() + void CheckUnstableUsage() const { + DCHECK(parent_deque_); + // Since circular_deque doesn't guarantee stability, any attempt to + // dereference this iterator after a mutation (i.e. the generation doesn't + // match the original) in the container is illegal. + DCHECK_EQ(created_generation_, parent_deque_->generation_) + << "circular_deque iterator dereferenced after mutation."; + } + void CheckComparable(const circular_deque_const_iterator& other) const { + DCHECK_EQ(parent_deque_, other.parent_deque_); + // Since circular_deque doesn't guarantee stability, two iterators that + // are compared must have been generated without mutating the container. + // If this fires, the container was mutated between generating the two + // iterators being compared. + DCHECK_EQ(created_generation_, other.created_generation_); + } +#else + inline void CheckUnstableUsage() const {} + inline void CheckComparable(const circular_deque_const_iterator&) const {} +#endif // DCHECK_IS_ON() + + const circular_deque* parent_deque_; + size_t index_; + +#if DCHECK_IS_ON() + // The generation of the parent deque when this iterator was created. The + // container will update the generation for every modification so we can + // test if the container was modified by comparing them. + uint64_t created_generation_; +#endif // DCHECK_IS_ON() +}; + +template +class circular_deque_iterator : public circular_deque_const_iterator { + using base = circular_deque_const_iterator; + + public: + friend class circular_deque; + + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + using iterator_category = std::random_access_iterator_tag; + + // Expose the base class' constructor. + circular_deque_iterator() : circular_deque_const_iterator() {} + + // Dereferencing. + T& operator*() const { return const_cast(base::operator*()); } + T* operator->() const { return const_cast(base::operator->()); } + T& operator[](difference_type i) { + return const_cast(base::operator[](i)); + } + + // Random access mutation. + friend circular_deque_iterator operator+(const circular_deque_iterator& iter, + difference_type offset) { + circular_deque_iterator ret = iter; + ret.Add(offset); + return ret; + } + circular_deque_iterator& operator+=(difference_type offset) { + base::Add(offset); + return *this; + } + friend circular_deque_iterator operator-(const circular_deque_iterator& iter, + difference_type offset) { + circular_deque_iterator ret = iter; + ret.Add(-offset); + return ret; + } + circular_deque_iterator& operator-=(difference_type offset) { + base::Add(-offset); + return *this; + } + + // Increment and decrement. + circular_deque_iterator& operator++() { + base::Increment(); + return *this; + } + circular_deque_iterator operator++(int) { + circular_deque_iterator ret = *this; + base::Increment(); + return ret; + } + circular_deque_iterator& operator--() { + base::Decrement(); + return *this; + } + circular_deque_iterator operator--(int) { + circular_deque_iterator ret = *this; + base::Decrement(); + return ret; + } + + private: + circular_deque_iterator(const circular_deque* parent, size_t index) + : circular_deque_const_iterator(parent, index) {} +}; + +} // namespace internal + +template +class circular_deque { + private: + using VectorBuffer = internal::VectorBuffer; + + public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + using iterator = internal::circular_deque_iterator; + using const_iterator = internal::circular_deque_const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + // --------------------------------------------------------------------------- + // Constructor + + constexpr circular_deque() = default; + + // Constructs with |count| copies of |value| or default constructed version. + circular_deque(size_type count) { resize(count); } + circular_deque(size_type count, const T& value) { resize(count, value); } + + // Range constructor. + template + circular_deque(InputIterator first, InputIterator last) { + assign(first, last); + } + + // Copy/move. + circular_deque(const circular_deque& other) : buffer_(other.size() + 1) { + assign(other.begin(), other.end()); + } + circular_deque(circular_deque&& other) noexcept + : buffer_(std::move(other.buffer_)), + begin_(other.begin_), + end_(other.end_) { + other.begin_ = 0; + other.end_ = 0; + } + + circular_deque(std::initializer_list init) { assign(init); } + + ~circular_deque() { DestructRange(begin_, end_); } + + // --------------------------------------------------------------------------- + // Assignments. + // + // All of these may invalidate iterators and references. + + circular_deque& operator=(const circular_deque& other) { + if (&other == this) + return *this; + + reserve(other.size()); + assign(other.begin(), other.end()); + return *this; + } + circular_deque& operator=(circular_deque&& other) noexcept { + if (&other == this) + return *this; + + // We're about to overwrite the buffer, so don't free it in clear to + // avoid doing it twice. + ClearRetainCapacity(); + buffer_ = std::move(other.buffer_); + begin_ = other.begin_; + end_ = other.end_; + + other.begin_ = 0; + other.end_ = 0; + + IncrementGeneration(); + return *this; + } + circular_deque& operator=(std::initializer_list ilist) { + reserve(ilist.size()); + assign(std::begin(ilist), std::end(ilist)); + return *this; + } + + void assign(size_type count, const value_type& value) { + ClearRetainCapacity(); + reserve(count); + for (size_t i = 0; i < count; i++) + emplace_back(value); + IncrementGeneration(); + } + + // This variant should be enabled only when InputIterator is an iterator. + template + typename std::enable_if<::base::internal::is_iterator::value, + void>::type + assign(InputIterator first, InputIterator last) { + // Possible future enhancement, dispatch on iterator tag type. For forward + // iterators we can use std::difference to preallocate the space required + // and only do one copy. + ClearRetainCapacity(); + for (; first != last; ++first) + emplace_back(*first); + IncrementGeneration(); + } + + void assign(std::initializer_list value) { + reserve(std::distance(value.begin(), value.end())); + assign(value.begin(), value.end()); + } + + // --------------------------------------------------------------------------- + // Accessors. + // + // Since this class assumes no exceptions, at() and operator[] are equivalent. + + const value_type& at(size_type i) const { + DCHECK(i < size()); + size_t right_size = buffer_.capacity() - begin_; + if (begin_ <= end_ || i < right_size) + return buffer_[begin_ + i]; + return buffer_[i - right_size]; + } + value_type& at(size_type i) { + return const_cast( + const_cast(this)->at(i)); + } + + value_type& operator[](size_type i) { return at(i); } + const value_type& operator[](size_type i) const { + return const_cast(this)->at(i); + } + + value_type& front() { + DCHECK(!empty()); + return buffer_[begin_]; + } + const value_type& front() const { + DCHECK(!empty()); + return buffer_[begin_]; + } + + value_type& back() { + DCHECK(!empty()); + return *(--end()); + } + const value_type& back() const { + DCHECK(!empty()); + return *(--end()); + } + + // --------------------------------------------------------------------------- + // Iterators. + + iterator begin() { return iterator(this, begin_); } + const_iterator begin() const { return const_iterator(this, begin_); } + const_iterator cbegin() const { return const_iterator(this, begin_); } + + iterator end() { return iterator(this, end_); } + const_iterator end() const { return const_iterator(this, end_); } + const_iterator cend() const { return const_iterator(this, end_); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator crbegin() const { return rbegin(); } + + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crend() const { return rend(); } + + // --------------------------------------------------------------------------- + // Memory management. + + // IMPORTANT NOTE ON reserve(...): This class implements auto-shrinking of + // the buffer when elements are deleted and there is "too much" wasted space. + // So if you call reserve() with a large size in anticipation of pushing many + // elements, but pop an element before the queue is full, the capacity you + // reserved may be lost. + // + // As a result, it's only worthwhile to call reserve() when you're adding + // many things at once with no intermediate operations. + void reserve(size_type new_capacity) { + if (new_capacity > capacity()) + SetCapacityTo(new_capacity); + } + + size_type capacity() const { + // One item is wasted to indicate end(). + return buffer_.capacity() == 0 ? 0 : buffer_.capacity() - 1; + } + + void shrink_to_fit() { + if (empty()) { + // Optimize empty case to really delete everything if there was + // something. + if (buffer_.capacity()) + buffer_ = VectorBuffer(); + } else { + SetCapacityTo(size()); + } + } + + // --------------------------------------------------------------------------- + // Size management. + + // This will additionally reset the capacity() to 0. + void clear() { + // This can't resize(0) because that requires a default constructor to + // compile, which not all contained classes may implement. + ClearRetainCapacity(); + buffer_ = VectorBuffer(); + } + + bool empty() const { return begin_ == end_; } + + size_type size() const { + if (begin_ <= end_) + return end_ - begin_; + return buffer_.capacity() - begin_ + end_; + } + + // When reducing size, the elements are deleted from the end. When expanding + // size, elements are added to the end with |value| or the default + // constructed version. Even when using resize(count) to shrink, a default + // constructor is required for the code to compile, even though it will not + // be called. + // + // There are two versions rather than using a default value to avoid + // creating a temporary when shrinking (when it's not needed). Plus if + // the default constructor is desired when expanding usually just calling it + // for each element is faster than making a default-constructed temporary and + // copying it. + void resize(size_type count) { + // SEE BELOW VERSION if you change this. The code is mostly the same. + if (count > size()) { + // This could be slighly more efficient but expanding a queue with + // identical elements is unusual and the extra computations of emplacing + // one-by-one will typically be small relative to calling the constructor + // for every item. + ExpandCapacityIfNecessary(count - size()); + while (size() < count) + emplace_back(); + } else if (count < size()) { + size_t new_end = (begin_ + count) % buffer_.capacity(); + DestructRange(new_end, end_); + end_ = new_end; + + ShrinkCapacityIfNecessary(); + } + IncrementGeneration(); + } + void resize(size_type count, const value_type& value) { + // SEE ABOVE VERSION if you change this. The code is mostly the same. + if (count > size()) { + ExpandCapacityIfNecessary(count - size()); + while (size() < count) + emplace_back(value); + } else if (count < size()) { + size_t new_end = (begin_ + count) % buffer_.capacity(); + DestructRange(new_end, end_); + end_ = new_end; + + ShrinkCapacityIfNecessary(); + } + IncrementGeneration(); + } + + // --------------------------------------------------------------------------- + // Insert and erase. + // + // Insertion and deletion in the middle is O(n) and invalidates all existing + // iterators. + // + // The implementation of insert isn't optimized as much as it could be. If + // the insertion requires that the buffer be grown, it will first be grown + // and everything moved, and then the items will be inserted, potentially + // moving some items twice. This simplifies the implemntation substantially + // and means less generated templatized code. Since this is an uncommon + // operation for deques, and already relatively slow, it doesn't seem worth + // the benefit to optimize this. + + void insert(const_iterator pos, size_type count, const T& value) { + ValidateIterator(pos); + + // Optimize insert at the beginning. + if (pos == begin()) { + ExpandCapacityIfNecessary(count); + for (size_t i = 0; i < count; i++) + push_front(value); + return; + } + + iterator insert_cur(this, pos.index_); + iterator insert_end; + MakeRoomFor(count, &insert_cur, &insert_end); + while (insert_cur < insert_end) { + new (&buffer_[insert_cur.index_]) T(value); + ++insert_cur; + } + + IncrementGeneration(); + } + + // This enable_if keeps this call from getting confused with the (pos, count, + // value) version when value is an integer. + template + typename std::enable_if<::base::internal::is_iterator::value, + void>::type + insert(const_iterator pos, InputIterator first, InputIterator last) { + ValidateIterator(pos); + + size_t inserted_items = std::distance(first, last); + if (inserted_items == 0) + return; // Can divide by 0 when doing modulo below, so return early. + + // Make a hole to copy the items into. + iterator insert_cur; + iterator insert_end; + if (pos == begin()) { + // Optimize insert at the beginning, nothing needs to be shifted and the + // hole is the |inserted_items| block immediately before |begin_|. + ExpandCapacityIfNecessary(inserted_items); + insert_end = begin(); + begin_ = + (begin_ + buffer_.capacity() - inserted_items) % buffer_.capacity(); + insert_cur = begin(); + } else { + insert_cur = iterator(this, pos.index_); + MakeRoomFor(inserted_items, &insert_cur, &insert_end); + } + + // Copy the items. + while (insert_cur < insert_end) { + new (&buffer_[insert_cur.index_]) T(*first); + ++insert_cur; + ++first; + } + + IncrementGeneration(); + } + + // These all return an iterator to the inserted item. Existing iterators will + // be invalidated. + iterator insert(const_iterator pos, const T& value) { + return emplace(pos, value); + } + iterator insert(const_iterator pos, T&& value) { + return emplace(pos, std::move(value)); + } + template + iterator emplace(const_iterator pos, Args&&... args) { + ValidateIterator(pos); + + // Optimize insert at beginning which doesn't require shifting. + if (pos == cbegin()) { + emplace_front(std::forward(args)...); + return begin(); + } + + // Do this before we make the new iterators we return. + IncrementGeneration(); + + iterator insert_begin(this, pos.index_); + iterator insert_end; + MakeRoomFor(1, &insert_begin, &insert_end); + new (&buffer_[insert_begin.index_]) T(std::forward(args)...); + + return insert_begin; + } + + // Calling erase() won't automatically resize the buffer smaller like resize + // or the pop functions. Erase is slow and relatively uncommon, and for + // normal deque usage a pop will normally be done on a regular basis that + // will prevent excessive buffer usage over long periods of time. It's not + // worth having the extra code for every template instantiation of erase() + // to resize capacity downward to a new buffer. + iterator erase(const_iterator pos) { return erase(pos, pos + 1); } + iterator erase(const_iterator first, const_iterator last) { + ValidateIterator(first); + ValidateIterator(last); + + IncrementGeneration(); + + // First, call the destructor on the deleted items. + if (first.index_ == last.index_) { + // Nothing deleted. Need to return early to avoid falling through to + // moving items on top of themselves. + return iterator(this, first.index_); + } else if (first.index_ < last.index_) { + // Contiguous range. + buffer_.DestructRange(&buffer_[first.index_], &buffer_[last.index_]); + } else { + // Deleted range wraps around. + buffer_.DestructRange(&buffer_[first.index_], + &buffer_[buffer_.capacity()]); + buffer_.DestructRange(&buffer_[0], &buffer_[last.index_]); + } + + if (first.index_ == begin_) { + // This deletion is from the beginning. Nothing needs to be copied, only + // begin_ needs to be updated. + begin_ = last.index_; + return iterator(this, last.index_); + } + + // In an erase operation, the shifted items all move logically to the left, + // so move them from left-to-right. + iterator move_src(this, last.index_); + iterator move_src_end = end(); + iterator move_dest(this, first.index_); + for (; move_src < move_src_end; move_src++, move_dest++) { + buffer_.MoveRange(&buffer_[move_src.index_], + &buffer_[move_src.index_ + 1], + &buffer_[move_dest.index_]); + } + + end_ = move_dest.index_; + + // Since we did not reallocate and only changed things after the erase + // element(s), the input iterator's index points to the thing following the + // deletion. + return iterator(this, first.index_); + } + + // --------------------------------------------------------------------------- + // Begin/end operations. + + void push_front(const T& value) { emplace_front(value); } + void push_front(T&& value) { emplace_front(std::move(value)); } + + void push_back(const T& value) { emplace_back(value); } + void push_back(T&& value) { emplace_back(std::move(value)); } + + template + reference emplace_front(Args&&... args) { + ExpandCapacityIfNecessary(1); + if (begin_ == 0) + begin_ = buffer_.capacity() - 1; + else + begin_--; + IncrementGeneration(); + new (&buffer_[begin_]) T(std::forward(args)...); + return front(); + } + + template + reference emplace_back(Args&&... args) { + ExpandCapacityIfNecessary(1); + new (&buffer_[end_]) T(std::forward(args)...); + if (end_ == buffer_.capacity() - 1) + end_ = 0; + else + end_++; + IncrementGeneration(); + return back(); + } + + void pop_front() { + DCHECK(size()); + buffer_.DestructRange(&buffer_[begin_], &buffer_[begin_ + 1]); + begin_++; + if (begin_ == buffer_.capacity()) + begin_ = 0; + + ShrinkCapacityIfNecessary(); + + // Technically popping will not invalidate any iterators since the + // underlying buffer will be stable. But in the future we may want to add a + // feature that resizes the buffer smaller if there is too much wasted + // space. This ensures we can make such a change safely. + IncrementGeneration(); + } + void pop_back() { + DCHECK(size()); + if (end_ == 0) + end_ = buffer_.capacity() - 1; + else + end_--; + buffer_.DestructRange(&buffer_[end_], &buffer_[end_ + 1]); + + ShrinkCapacityIfNecessary(); + + // See pop_front comment about why this is here. + IncrementGeneration(); + } + + // --------------------------------------------------------------------------- + // General operations. + + void swap(circular_deque& other) { + std::swap(buffer_, other.buffer_); + std::swap(begin_, other.begin_); + std::swap(end_, other.end_); + IncrementGeneration(); + } + + friend void swap(circular_deque& lhs, circular_deque& rhs) { lhs.swap(rhs); } + + private: + friend internal::circular_deque_iterator; + friend internal::circular_deque_const_iterator; + + // Moves the items in the given circular buffer to the current one. The + // source is moved from so will become invalid. The destination buffer must + // have already been allocated with enough size. + static void MoveBuffer(VectorBuffer& from_buf, + size_t from_begin, + size_t from_end, + VectorBuffer* to_buf, + size_t* to_begin, + size_t* to_end) { + size_t from_capacity = from_buf.capacity(); + + *to_begin = 0; + if (from_begin < from_end) { + // Contiguous. + from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_end], + to_buf->begin()); + *to_end = from_end - from_begin; + } else if (from_begin > from_end) { + // Discontiguous, copy the right side to the beginning of the new buffer. + from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_capacity], + to_buf->begin()); + size_t right_size = from_capacity - from_begin; + // Append the left side. + from_buf.MoveRange(&from_buf[0], &from_buf[from_end], + &(*to_buf)[right_size]); + *to_end = right_size + from_end; + } else { + // No items. + *to_end = 0; + } + } + + // Expands the buffer size. This assumes the size is larger than the + // number of elements in the vector (it won't call delete on anything). + void SetCapacityTo(size_t new_capacity) { + // Use the capacity + 1 as the internal buffer size to differentiate + // empty and full (see definition of buffer_ below). + VectorBuffer new_buffer(new_capacity + 1); + MoveBuffer(buffer_, begin_, end_, &new_buffer, &begin_, &end_); + buffer_ = std::move(new_buffer); + } + void ExpandCapacityIfNecessary(size_t additional_elts) { + size_t min_new_capacity = size() + additional_elts; + if (capacity() >= min_new_capacity) + return; // Already enough room. + + min_new_capacity = + std::max(min_new_capacity, internal::kCircularBufferInitialCapacity); + + // std::vector always grows by at least 50%. WTF::Deque grows by at least + // 25%. We expect queue workloads to generally stay at a similar size and + // grow less than a vector might, so use 25%. + size_t new_capacity = + std::max(min_new_capacity, capacity() + capacity() / 4); + SetCapacityTo(new_capacity); + } + + void ShrinkCapacityIfNecessary() { + // Don't auto-shrink below this size. + if (capacity() <= internal::kCircularBufferInitialCapacity) + return; + + // Shrink when 100% of the size() is wasted. + size_t sz = size(); + size_t empty_spaces = capacity() - sz; + if (empty_spaces < sz) + return; + + // Leave 1/4 the size as free capacity, not going below the initial + // capacity. + size_t new_capacity = + std::max(internal::kCircularBufferInitialCapacity, sz + sz / 4); + if (new_capacity < capacity()) { + // Count extra item to convert to internal capacity. + SetCapacityTo(new_capacity); + } + } + + // Backend for clear() but does not resize the internal buffer. + void ClearRetainCapacity() { + // This can't resize(0) because that requires a default constructor to + // compile, which not all contained classes may implement. + DestructRange(begin_, end_); + begin_ = 0; + end_ = 0; + IncrementGeneration(); + } + + // Calls destructors for the given begin->end indices. The indices may wrap + // around. The buffer is not resized, and the begin_ and end_ members are + // not changed. + void DestructRange(size_t begin, size_t end) { + if (end == begin) { + return; + } else if (end > begin) { + buffer_.DestructRange(&buffer_[begin], &buffer_[end]); + } else { + buffer_.DestructRange(&buffer_[begin], &buffer_[buffer_.capacity()]); + buffer_.DestructRange(&buffer_[0], &buffer_[end]); + } + } + + // Makes room for |count| items starting at |*insert_begin|. Since iterators + // are not stable across buffer resizes, |*insert_begin| will be updated to + // point to the beginning of the newly opened position in the new array (it's + // in/out), and the end of the newly opened position (it's out-only). + void MakeRoomFor(size_t count, iterator* insert_begin, iterator* insert_end) { + if (count == 0) { + *insert_end = *insert_begin; + return; + } + + // The offset from the beginning will be stable across reallocations. + size_t begin_offset = insert_begin->OffsetFromBegin(); + ExpandCapacityIfNecessary(count); + + insert_begin->index_ = (begin_ + begin_offset) % buffer_.capacity(); + *insert_end = + iterator(this, (insert_begin->index_ + count) % buffer_.capacity()); + + // Update the new end and prepare the iterators for copying. + iterator src = end(); + end_ = (end_ + count) % buffer_.capacity(); + iterator dest = end(); + + // Move the elements. This will always involve shifting logically to the + // right, so move in a right-to-left order. + while (true) { + if (src == *insert_begin) + break; + --src; + --dest; + buffer_.MoveRange(&buffer_[src.index_], &buffer_[src.index_ + 1], + &buffer_[dest.index_]); + } + } + +#if DCHECK_IS_ON() + // Asserts the given index is dereferencable. The index is an index into the + // buffer, not an index used by operator[] or at() which will be offsets from + // begin. + void CheckValidIndex(size_t i) const { + if (begin_ <= end_) + DCHECK(i >= begin_ && i < end_); + else + DCHECK((i >= begin_ && i < buffer_.capacity()) || i < end_); + } + + // Asserts the given index is either dereferencable or points to end(). + void CheckValidIndexOrEnd(size_t i) const { + if (i != end_) + CheckValidIndex(i); + } + + void ValidateIterator(const const_iterator& i) const { + DCHECK(i.parent_deque_ == this); + i.CheckUnstableUsage(); + } + + // See generation_ below. + void IncrementGeneration() { generation_++; } +#else + // No-op versions of these functions for release builds. + void CheckValidIndex(size_t) const {} + void CheckValidIndexOrEnd(size_t) const {} + void ValidateIterator(const const_iterator& i) const {} + void IncrementGeneration() {} +#endif + + // Danger, the buffer_.capacity() is the "internal capacity" which is + // capacity() + 1 since there is an extra item to indicate the end. Otherwise + // being completely empty and completely full are indistinguishable (begin == + // end). We could add a separate flag to avoid it, but that adds significant + // extra complexity since every computation will have to check for it. Always + // keeping one extra unused element in the buffer makes iterator computations + // much simpler. + // + // Container internal code will want to use buffer_.capacity() for offset + // computations rather than capacity(). + VectorBuffer buffer_; + size_type begin_ = 0; + size_type end_ = 0; + +#if DCHECK_IS_ON() + // Incremented every time a modification is made that could affect iterator + // invalidations. + uint64_t generation_ = 0; +#endif +}; + +// Implementations of base::Erase[If] (see base/stl_util.h). +template +void Erase(circular_deque& container, const Value& value) { + container.erase(std::remove(container.begin(), container.end(), value), + container.end()); +} + +template +void EraseIf(circular_deque& container, Predicate pred) { + container.erase(std::remove_if(container.begin(), container.end(), pred), + container.end()); +} + +} // namespace base + +#endif // BASE_CONTAINERS_CIRCULAR_DEQUE_H_ diff --git a/src/3rdparty/gn/base/containers/flat_map.h b/src/3rdparty/gn/base/containers/flat_map.h new file mode 100644 index 00000000000..b4fe5196ec8 --- /dev/null +++ b/src/3rdparty/gn/base/containers/flat_map.h @@ -0,0 +1,362 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_FLAT_MAP_H_ +#define BASE_CONTAINERS_FLAT_MAP_H_ + +#include +#include +#include + +#include "base/containers/flat_tree.h" +#include "base/logging.h" +#include "base/template_util.h" + +namespace base { + +namespace internal { + +// An implementation of the flat_tree GetKeyFromValue template parameter that +// extracts the key as the first element of a pair. +template +struct GetKeyFromValuePairFirst { + const Key& operator()(const std::pair& p) const { + return p.first; + } +}; + +} // namespace internal + +// flat_map is a container with a std::map-like interface that stores its +// contents in a sorted vector. +// +// Please see //base/containers/README.md for an overview of which container +// to select. +// +// PROS +// +// - Good memory locality. +// - Low overhead, especially for smaller maps. +// - Performance is good for more workloads than you might expect (see +// overview link above). +// - Supports C++14 map interface. +// +// CONS +// +// - Inserts and removals are O(n). +// +// IMPORTANT NOTES +// +// - Iterators are invalidated across mutations. +// - If possible, construct a flat_map in one operation by inserting into +// a std::vector and moving that vector into the flat_map constructor. +// +// QUICK REFERENCE +// +// Most of the core functionality is inherited from flat_tree. Please see +// flat_tree.h for more details for most of these functions. As a quick +// reference, the functions available are: +// +// Constructors (inputs need not be sorted): +// flat_map(InputIterator first, InputIterator last, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES, +// const Compare& compare = Compare()); +// flat_map(const flat_map&); +// flat_map(flat_map&&); +// flat_map(std::vector, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES, +// const Compare& compare = Compare()); // Re-use storage. +// flat_map(std::initializer_list ilist, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES, +// const Compare& comp = Compare()); +// +// Assignment functions: +// flat_map& operator=(const flat_map&); +// flat_map& operator=(flat_map&&); +// flat_map& operator=(initializer_list); +// +// Memory management functions: +// void reserve(size_t); +// size_t capacity() const; +// void shrink_to_fit(); +// +// Size management functions: +// void clear(); +// size_t size() const; +// size_t max_size() const; +// bool empty() const; +// +// Iterator functions: +// iterator begin(); +// const_iterator begin() const; +// const_iterator cbegin() const; +// iterator end(); +// const_iterator end() const; +// const_iterator cend() const; +// reverse_iterator rbegin(); +// const reverse_iterator rbegin() const; +// const_reverse_iterator crbegin() const; +// reverse_iterator rend(); +// const_reverse_iterator rend() const; +// const_reverse_iterator crend() const; +// +// Insert and accessor functions: +// mapped_type& operator[](const key_type&); +// mapped_type& operator[](key_type&&); +// pair insert(const value_type&); +// pair insert(value_type&&); +// iterator insert(const_iterator hint, const value_type&); +// iterator insert(const_iterator hint, value_type&&); +// void insert(InputIterator first, InputIterator last, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES); +// pair insert_or_assign(K&&, M&&); +// iterator insert_or_assign(const_iterator hint, K&&, M&&); +// pair emplace(Args&&...); +// iterator emplace_hint(const_iterator, Args&&...); +// pair try_emplace(K&&, Args&&...); +// iterator try_emplace(const_iterator hint, K&&, Args&&...); +// +// Erase functions: +// iterator erase(iterator); +// iterator erase(const_iterator); +// iterator erase(const_iterator first, const_iterator& last); +// template size_t erase(const K& key); +// +// Comparators (see std::map documentation). +// key_compare key_comp() const; +// value_compare value_comp() const; +// +// Search functions: +// template size_t count(const K&) const; +// template iterator find(const K&); +// template const_iterator find(const K&) const; +// template pair equal_range(const K&); +// template iterator lower_bound(const K&); +// template const_iterator lower_bound(const K&) const; +// template iterator upper_bound(const K&); +// template const_iterator upper_bound(const K&) const; +// +// General functions: +// void swap(flat_map&&); +// +// Non-member operators: +// bool operator==(const flat_map&, const flat_map); +// bool operator!=(const flat_map&, const flat_map); +// bool operator<(const flat_map&, const flat_map); +// bool operator>(const flat_map&, const flat_map); +// bool operator>=(const flat_map&, const flat_map); +// bool operator<=(const flat_map&, const flat_map); +// +template > +class flat_map : public ::base::internal::flat_tree< + Key, + std::pair, + ::base::internal::GetKeyFromValuePairFirst, + Compare> { + private: + using tree = typename ::base::internal::flat_tree< + Key, + std::pair, + ::base::internal::GetKeyFromValuePairFirst, + Compare>; + + public: + using key_type = typename tree::key_type; + using mapped_type = Mapped; + using value_type = typename tree::value_type; + using iterator = typename tree::iterator; + using const_iterator = typename tree::const_iterator; + + // -------------------------------------------------------------------------- + // Lifetime and assignments. + // + // Note: we could do away with these constructors, destructor and assignment + // operator overloads by inheriting |tree|'s, but this breaks the GCC build + // due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84782 (see + // https://crbug.com/837221). + + flat_map() = default; + explicit flat_map(const Compare& comp); + + template + flat_map(InputIterator first, + InputIterator last, + FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, + const Compare& comp = Compare()); + + flat_map(const flat_map&) = default; + flat_map(flat_map&&) noexcept = default; + + flat_map(std::vector items, + FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, + const Compare& comp = Compare()); + + flat_map(std::initializer_list ilist, + FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, + const Compare& comp = Compare()); + + ~flat_map() = default; + + flat_map& operator=(const flat_map&) = default; + flat_map& operator=(flat_map&&) = default; + // Takes the first if there are duplicates in the initializer list. + flat_map& operator=(std::initializer_list ilist); + + // -------------------------------------------------------------------------- + // Map-specific insert operations. + // + // Normal insert() functions are inherited from flat_tree. + // + // Assume that every operation invalidates iterators and references. + // Insertion of one element can take O(size). + + mapped_type& operator[](const key_type& key); + mapped_type& operator[](key_type&& key); + + template + std::pair insert_or_assign(K&& key, M&& obj); + template + iterator insert_or_assign(const_iterator hint, K&& key, M&& obj); + + template + std::enable_if_t::value, + std::pair> + try_emplace(K&& key, Args&&... args); + + template + std::enable_if_t::value, iterator> + try_emplace(const_iterator hint, K&& key, Args&&... args); + + // -------------------------------------------------------------------------- + // General operations. + // + // Assume that swap invalidates iterators and references. + + void swap(flat_map& other) noexcept; + + friend void swap(flat_map& lhs, flat_map& rhs) noexcept { lhs.swap(rhs); } +}; + +// ---------------------------------------------------------------------------- +// Lifetime. + +template +flat_map::flat_map(const Compare& comp) : tree(comp) {} + +template +template +flat_map::flat_map(InputIterator first, + InputIterator last, + FlatContainerDupes dupe_handling, + const Compare& comp) + : tree(first, last, dupe_handling, comp) {} + +template +flat_map::flat_map(std::vector items, + FlatContainerDupes dupe_handling, + const Compare& comp) + : tree(std::move(items), dupe_handling, comp) {} + +template +flat_map::flat_map( + std::initializer_list ilist, + FlatContainerDupes dupe_handling, + const Compare& comp) + : flat_map(std::begin(ilist), std::end(ilist), dupe_handling, comp) {} + +// ---------------------------------------------------------------------------- +// Assignments. + +template +auto flat_map::operator=( + std::initializer_list ilist) -> flat_map& { + // When https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84782 gets fixed, we + // need to remember to inherit tree::operator= to prevent + // flat_map<...> x; + // x = {...}; + // from first creating a flat_map and then move assigning it. This most + // likely would be optimized away but still affects our debug builds. + tree::operator=(ilist); + return *this; +} + +// ---------------------------------------------------------------------------- +// Insert operations. + +template +auto flat_map::operator[](const key_type& key) + -> mapped_type& { + iterator found = tree::lower_bound(key); + if (found == tree::end() || tree::key_comp()(key, found->first)) + found = tree::unsafe_emplace(found, key, mapped_type()); + return found->second; +} + +template +auto flat_map::operator[](key_type&& key) + -> mapped_type& { + iterator found = tree::lower_bound(key); + if (found == tree::end() || tree::key_comp()(key, found->first)) + found = tree::unsafe_emplace(found, std::move(key), mapped_type()); + return found->second; +} + +template +template +auto flat_map::insert_or_assign(K&& key, M&& obj) + -> std::pair { + auto result = + tree::emplace_key_args(key, std::forward(key), std::forward(obj)); + if (!result.second) + result.first->second = std::forward(obj); + return result; +} + +template +template +auto flat_map::insert_or_assign(const_iterator hint, + K&& key, + M&& obj) -> iterator { + auto result = tree::emplace_hint_key_args(hint, key, std::forward(key), + std::forward(obj)); + if (!result.second) + result.first->second = std::forward(obj); + return result.first; +} + +template +template +auto flat_map::try_emplace(K&& key, Args&&... args) + -> std::enable_if_t::value, + std::pair> { + return tree::emplace_key_args( + key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)); +} + +template +template +auto flat_map::try_emplace(const_iterator hint, + K&& key, + Args&&... args) + -> std::enable_if_t::value, iterator> { + return tree::emplace_hint_key_args( + hint, key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)) + .first; +} + +// ---------------------------------------------------------------------------- +// General operations. + +template +void flat_map::swap(flat_map& other) noexcept { + tree::swap(other); +} + +} // namespace base + +#endif // BASE_CONTAINERS_FLAT_MAP_H_ diff --git a/src/3rdparty/gn/base/containers/flat_set.h b/src/3rdparty/gn/base/containers/flat_set.h new file mode 100644 index 00000000000..700617f2825 --- /dev/null +++ b/src/3rdparty/gn/base/containers/flat_set.h @@ -0,0 +1,141 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_FLAT_SET_H_ +#define BASE_CONTAINERS_FLAT_SET_H_ + +#include + +#include "base/containers/flat_tree.h" +#include "base/template_util.h" + +namespace base { + +// flat_set is a container with a std::set-like interface that stores its +// contents in a sorted vector. +// +// Please see //base/containers/README.md for an overview of which container +// to select. +// +// PROS +// +// - Good memory locality. +// - Low overhead, especially for smaller sets. +// - Performance is good for more workloads than you might expect (see +// overview link above). +// - Supports C++14 set interface. +// +// CONS +// +// - Inserts and removals are O(n). +// +// IMPORTANT NOTES +// +// - Iterators are invalidated across mutations. +// - If possible, construct a flat_set in one operation by inserting into +// a std::vector and moving that vector into the flat_set constructor. +// - For multiple removals use base::EraseIf() which is O(n) rather than +// O(n * removed_items). +// +// QUICK REFERENCE +// +// Most of the core functionality is inherited from flat_tree. Please see +// flat_tree.h for more details for most of these functions. As a quick +// reference, the functions available are: +// +// Constructors (inputs need not be sorted): +// flat_set(InputIterator first, InputIterator last, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES, +// const Compare& compare = Compare()); +// flat_set(const flat_set&); +// flat_set(flat_set&&); +// flat_set(std::vector, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES, +// const Compare& compare = Compare()); // Re-use storage. +// flat_set(std::initializer_list ilist, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES, +// const Compare& comp = Compare()); +// +// Assignment functions: +// flat_set& operator=(const flat_set&); +// flat_set& operator=(flat_set&&); +// flat_set& operator=(initializer_list); +// +// Memory management functions: +// void reserve(size_t); +// size_t capacity() const; +// void shrink_to_fit(); +// +// Size management functions: +// void clear(); +// size_t size() const; +// size_t max_size() const; +// bool empty() const; +// +// Iterator functions: +// iterator begin(); +// const_iterator begin() const; +// const_iterator cbegin() const; +// iterator end(); +// const_iterator end() const; +// const_iterator cend() const; +// reverse_iterator rbegin(); +// const reverse_iterator rbegin() const; +// const_reverse_iterator crbegin() const; +// reverse_iterator rend(); +// const_reverse_iterator rend() const; +// const_reverse_iterator crend() const; +// +// Insert and accessor functions: +// pair insert(const key_type&); +// pair insert(key_type&&); +// void insert(InputIterator first, InputIterator last, +// FlatContainerDupes = KEEP_FIRST_OF_DUPES); +// iterator insert(const_iterator hint, const key_type&); +// iterator insert(const_iterator hint, key_type&&); +// pair emplace(Args&&...); +// iterator emplace_hint(const_iterator, Args&&...); +// +// Erase functions: +// iterator erase(iterator); +// iterator erase(const_iterator); +// iterator erase(const_iterator first, const_iterator& last); +// template size_t erase(const K& key); +// +// Comparators (see std::set documentation). +// key_compare key_comp() const; +// value_compare value_comp() const; +// +// Search functions: +// template size_t count(const K&) const; +// template iterator find(const K&); +// template const_iterator find(const K&) const; +// template bool contains(const K&) const; +// template pair equal_range(K&); +// template iterator lower_bound(const K&); +// template const_iterator lower_bound(const K&) const; +// template iterator upper_bound(const K&); +// template const_iterator upper_bound(const K&) const; +// +// General functions: +// void swap(flat_set&&); +// +// Non-member operators: +// bool operator==(const flat_set&, const flat_set); +// bool operator!=(const flat_set&, const flat_set); +// bool operator<(const flat_set&, const flat_set); +// bool operator>(const flat_set&, const flat_set); +// bool operator>=(const flat_set&, const flat_set); +// bool operator<=(const flat_set&, const flat_set); +// +template > +using flat_set = typename ::base::internal::flat_tree< + Key, + Key, + ::base::internal::GetKeyFromValueIdentity, + Compare>; + +} // namespace base + +#endif // BASE_CONTAINERS_FLAT_SET_H_ \ No newline at end of file diff --git a/src/3rdparty/gn/base/containers/flat_tree.h b/src/3rdparty/gn/base/containers/flat_tree.h new file mode 100644 index 00000000000..7856e242336 --- /dev/null +++ b/src/3rdparty/gn/base/containers/flat_tree.h @@ -0,0 +1,1004 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_FLAT_TREE_H_ +#define BASE_CONTAINERS_FLAT_TREE_H_ + +#include +#include +#include +#include + +#include "base/template_util.h" + +namespace base { + +enum FlatContainerDupes { + KEEP_FIRST_OF_DUPES, + KEEP_LAST_OF_DUPES, +}; + +namespace internal { + +// This is a convenience method returning true if Iterator is at least a +// ForwardIterator and thus supports multiple passes over a range. +template +constexpr bool is_multipass() { + return std::is_base_of< + std::forward_iterator_tag, + typename std::iterator_traits::iterator_category>::value; +} + +// This algorithm is like unique() from the standard library except it +// selects only the last of consecutive values instead of the first. +template +Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) { + Iterator replacable = std::adjacent_find(first, last, compare); + + // No duplicate elements found. + if (replacable == last) + return last; + + first = std::next(replacable); + + // Last element is a duplicate but all others are unique. + if (first == last) + return replacable; + + // This loop is based on std::adjacent_find but std::adjacent_find doesn't + // quite cut it. + for (Iterator next = std::next(first); next != last; ++next, ++first) { + if (!compare(*first, *next)) + *replacable++ = std::move(*first); + } + + // Last element should be copied unconditionally. + *replacable++ = std::move(*first); + return replacable; +} + +// Uses SFINAE to detect whether type has is_transparent member. +template +struct IsTransparentCompare : std::false_type {}; +template +struct IsTransparentCompare> + : std::true_type {}; + +// Implementation ------------------------------------------------------------- + +// Implementation of a sorted vector for backing flat_set and flat_map. Do not +// use directly. +// +// The use of "value" in this is like std::map uses, meaning it's the thing +// contained (in the case of map it's a pair). The Key is how +// things are looked up. In the case of a set, Key == Value. In the case of +// a map, the Key is a component of a Value. +// +// The helper class GetKeyFromValue provides the means to extract a key from a +// value for comparison purposes. It should implement: +// const Key& operator()(const Value&). +template +class flat_tree { + private: + using underlying_type = std::vector; + + public: + // -------------------------------------------------------------------------- + // Types. + // + using key_type = Key; + using key_compare = KeyCompare; + using value_type = Value; + + // Wraps the templated key comparison to compare values. + class value_compare : public key_compare { + public: + value_compare() = default; + + template + explicit value_compare(Cmp&& compare_arg) + : KeyCompare(std::forward(compare_arg)) {} + + bool operator()(const value_type& left, const value_type& right) const { + GetKeyFromValue extractor; + return key_compare::operator()(extractor(left), extractor(right)); + } + }; + + using pointer = typename underlying_type::pointer; + using const_pointer = typename underlying_type::const_pointer; + using reference = typename underlying_type::reference; + using const_reference = typename underlying_type::const_reference; + using size_type = typename underlying_type::size_type; + using difference_type = typename underlying_type::difference_type; + using iterator = typename underlying_type::iterator; + using const_iterator = typename underlying_type::const_iterator; + using reverse_iterator = typename underlying_type::reverse_iterator; + using const_reverse_iterator = + typename underlying_type::const_reverse_iterator; + + // -------------------------------------------------------------------------- + // Lifetime. + // + // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity + // and take O(N * log(N)) + O(N) if extra memory is available (N is a range + // length). + // + // Assume that move constructors invalidate iterators and references. + // + // The constructors that take ranges, lists, and vectors do not require that + // the input be sorted. + + flat_tree(); + explicit flat_tree(const key_compare& comp); + + template + flat_tree(InputIterator first, + InputIterator last, + FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, + const key_compare& comp = key_compare()); + + flat_tree(const flat_tree&); + flat_tree(flat_tree&&) noexcept = default; + + flat_tree(std::vector items, + FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, + const key_compare& comp = key_compare()); + + flat_tree(std::initializer_list ilist, + FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, + const key_compare& comp = key_compare()); + + ~flat_tree(); + + // -------------------------------------------------------------------------- + // Assignments. + // + // Assume that move assignment invalidates iterators and references. + + flat_tree& operator=(const flat_tree&); + flat_tree& operator=(flat_tree&&); + // Takes the first if there are duplicates in the initializer list. + flat_tree& operator=(std::initializer_list ilist); + + // -------------------------------------------------------------------------- + // Memory management. + // + // Beware that shrink_to_fit() simply forwards the request to the + // underlying_type and its implementation is free to optimize otherwise and + // leave capacity() to be greater that its size. + // + // reserve() and shrink_to_fit() invalidate iterators and references. + + void reserve(size_type new_capacity); + size_type capacity() const; + void shrink_to_fit(); + + // -------------------------------------------------------------------------- + // Size management. + // + // clear() leaves the capacity() of the flat_tree unchanged. + + void clear(); + + size_type size() const; + size_type max_size() const; + bool empty() const; + + // -------------------------------------------------------------------------- + // Iterators. + + iterator begin(); + const_iterator begin() const; + const_iterator cbegin() const; + + iterator end(); + const_iterator end() const; + const_iterator cend() const; + + reverse_iterator rbegin(); + const_reverse_iterator rbegin() const; + const_reverse_iterator crbegin() const; + + reverse_iterator rend(); + const_reverse_iterator rend() const; + const_reverse_iterator crend() const; + + // -------------------------------------------------------------------------- + // Insert operations. + // + // Assume that every operation invalidates iterators and references. + // Insertion of one element can take O(size). Capacity of flat_tree grows in + // an implementation-defined manner. + // + // NOTE: Prefer to build a new flat_tree from a std::vector (or similar) + // instead of calling insert() repeatedly. + + std::pair insert(const value_type& val); + std::pair insert(value_type&& val); + + iterator insert(const_iterator position_hint, const value_type& x); + iterator insert(const_iterator position_hint, value_type&& x); + + // This method inserts the values from the range [first, last) into the + // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can + // overwrite existing values. + template + void insert(InputIterator first, + InputIterator last, + FlatContainerDupes dupes = KEEP_FIRST_OF_DUPES); + + template + std::pair emplace(Args&&... args); + + template + iterator emplace_hint(const_iterator position_hint, Args&&... args); + + // -------------------------------------------------------------------------- + // Erase operations. + // + // Assume that every operation invalidates iterators and references. + // + // erase(position), erase(first, last) can take O(size). + // erase(key) may take O(size) + O(log(size)). + // + // Prefer base::EraseIf() or some other variation on erase(remove(), end()) + // idiom when deleting multiple non-consecutive elements. + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + template + size_type erase(const K& key); + + // -------------------------------------------------------------------------- + // Comparators. + + key_compare key_comp() const; + value_compare value_comp() const; + + // -------------------------------------------------------------------------- + // Search operations. + // + // Search operations have O(log(size)) complexity. + + template + size_type count(const K& key) const; + + template + iterator find(const K& key); + + template + const_iterator find(const K& key) const; + + template + std::pair equal_range(const K& key); + + template + std::pair equal_range(const K& key) const; + + template + iterator lower_bound(const K& key); + + template + const_iterator lower_bound(const K& key) const; + + template + iterator upper_bound(const K& key); + + template + const_iterator upper_bound(const K& key) const; + + // -------------------------------------------------------------------------- + // General operations. + // + // Assume that swap invalidates iterators and references. + // + // Implementation note: currently we use operator==() and operator<() on + // std::vector, because they have the same contract we need, so we use them + // directly for brevity and in case it is more optimal than calling equal() + // and lexicograhpical_compare(). If the underlying container type is changed, + // this code may need to be modified. + + void swap(flat_tree& other) noexcept; + + friend bool operator==(const flat_tree& lhs, const flat_tree& rhs) { + return lhs.impl_.body_ == rhs.impl_.body_; + } + + friend bool operator!=(const flat_tree& lhs, const flat_tree& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const flat_tree& lhs, const flat_tree& rhs) { + return lhs.impl_.body_ < rhs.impl_.body_; + } + + friend bool operator>(const flat_tree& lhs, const flat_tree& rhs) { + return rhs < lhs; + } + + friend bool operator>=(const flat_tree& lhs, const flat_tree& rhs) { + return !(lhs < rhs); + } + + friend bool operator<=(const flat_tree& lhs, const flat_tree& rhs) { + return !(lhs > rhs); + } + + friend void swap(flat_tree& lhs, flat_tree& rhs) noexcept { lhs.swap(rhs); } + + protected: + // Emplaces a new item into the tree that is known not to be in it. This + // is for implementing map operator[]. + template + iterator unsafe_emplace(const_iterator position, Args&&... args); + + // Attempts to emplace a new element with key |key|. Only if |key| is not yet + // present, construct value_type from |args| and insert it. Returns an + // iterator to the element with key |key| and a bool indicating whether an + // insertion happened. + template + std::pair emplace_key_args(const K& key, Args&&... args); + + // Similar to |emplace_key_args|, but checks |hint| first as a possible + // insertion position. + template + std::pair emplace_hint_key_args(const_iterator hint, + const K& key, + Args&&... args); + + private: + // Helper class for e.g. lower_bound that can compare a value on the left + // to a key on the right. + struct KeyValueCompare { + // The key comparison object must outlive this class. + explicit KeyValueCompare(const key_compare& key_comp) + : key_comp_(key_comp) {} + + template + bool operator()(const T& lhs, const U& rhs) const { + return key_comp_(extract_if_value_type(lhs), extract_if_value_type(rhs)); + } + + private: + const key_type& extract_if_value_type(const value_type& v) const { + GetKeyFromValue extractor; + return extractor(v); + } + + template + const K& extract_if_value_type(const K& k) const { + return k; + } + + const key_compare& key_comp_; + }; + + const flat_tree& as_const() { return *this; } + + iterator const_cast_it(const_iterator c_it) { + auto distance = std::distance(cbegin(), c_it); + return std::next(begin(), distance); + } + + // This method is inspired by both std::map::insert(P&&) and + // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent + // element is not present yet, otherwise it overwrites. It returns an iterator + // to the modified element and a flag indicating whether insertion or + // assignment happened. + template + std::pair insert_or_assign(V&& val) { + auto position = lower_bound(GetKeyFromValue()(val)); + + if (position == end() || value_comp()(val, *position)) + return {impl_.body_.emplace(position, std::forward(val)), true}; + + *position = std::forward(val); + return {position, false}; + } + + // This method is similar to insert_or_assign, with the following differences: + // - Instead of searching [begin(), end()) it only searches [first, last). + // - In case no equivalent element is found, val is appended to the end of the + // underlying body and an iterator to the next bigger element in [first, + // last) is returned. + template + std::pair append_or_assign(iterator first, + iterator last, + V&& val) { + auto position = std::lower_bound(first, last, val, value_comp()); + + if (position == last || value_comp()(val, *position)) { + // emplace_back might invalidate position, which is why distance needs to + // be cached. + const difference_type distance = std::distance(begin(), position); + impl_.body_.emplace_back(std::forward(val)); + return {std::next(begin(), distance), true}; + } + + *position = std::forward(val); + return {position, false}; + } + + // This method is similar to insert, with the following differences: + // - Instead of searching [begin(), end()) it only searches [first, last). + // - In case no equivalent element is found, val is appended to the end of the + // underlying body and an iterator to the next bigger element in [first, + // last) is returned. + template + std::pair append_unique(iterator first, + iterator last, + V&& val) { + auto position = std::lower_bound(first, last, val, value_comp()); + + if (position == last || value_comp()(val, *position)) { + // emplace_back might invalidate position, which is why distance needs to + // be cached. + const difference_type distance = std::distance(begin(), position); + impl_.body_.emplace_back(std::forward(val)); + return {std::next(begin(), distance), true}; + } + + return {position, false}; + } + + void sort_and_unique(iterator first, + iterator last, + FlatContainerDupes dupes) { + // Preserve stability for the unique code below. + std::stable_sort(first, last, impl_.get_value_comp()); + + auto comparator = [this](const value_type& lhs, const value_type& rhs) { + // lhs is already <= rhs due to sort, therefore + // !(lhs < rhs) <=> lhs == rhs. + return !impl_.get_value_comp()(lhs, rhs); + }; + + iterator erase_after; + switch (dupes) { + case KEEP_FIRST_OF_DUPES: + erase_after = std::unique(first, last, comparator); + break; + case KEEP_LAST_OF_DUPES: + erase_after = LastUnique(first, last, comparator); + break; + } + erase(erase_after, last); + } + + // To support comparators that may not be possible to default-construct, we + // have to store an instance of Compare. Using this to store all internal + // state of flat_tree and using private inheritance to store compare lets us + // take advantage of an empty base class optimization to avoid extra space in + // the common case when Compare has no state. + struct Impl : private value_compare { + Impl() = default; + + template + explicit Impl(Cmp&& compare_arg, Body&&... underlying_type_args) + : value_compare(std::forward(compare_arg)), + body_(std::forward(underlying_type_args)...) {} + + const value_compare& get_value_comp() const { return *this; } + const key_compare& get_key_comp() const { return *this; } + + underlying_type body_; + } impl_; + + // If the compare is not transparent we want to construct key_type once. + template + using KeyTypeOrK = typename std:: + conditional::value, K, key_type>::type; +}; + +// ---------------------------------------------------------------------------- +// Lifetime. + +template +flat_tree::flat_tree() = default; + +template +flat_tree::flat_tree( + const KeyCompare& comp) + : impl_(comp) {} + +template +template +flat_tree::flat_tree( + InputIterator first, + InputIterator last, + FlatContainerDupes dupe_handling, + const KeyCompare& comp) + : impl_(comp, first, last) { + sort_and_unique(begin(), end(), dupe_handling); +} + +template +flat_tree::flat_tree( + const flat_tree&) = default; + +template +flat_tree::flat_tree( + std::vector items, + FlatContainerDupes dupe_handling, + const KeyCompare& comp) + : impl_(comp, std::move(items)) { + sort_and_unique(begin(), end(), dupe_handling); +} + +template +flat_tree::flat_tree( + std::initializer_list ilist, + FlatContainerDupes dupe_handling, + const KeyCompare& comp) + : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {} + +template +flat_tree::~flat_tree() = default; + +// ---------------------------------------------------------------------------- +// Assignments. + +template +auto flat_tree::operator=( + const flat_tree&) -> flat_tree& = default; + +template +auto flat_tree::operator=(flat_tree &&) + -> flat_tree& = default; + +template +auto flat_tree::operator=( + std::initializer_list ilist) -> flat_tree& { + impl_.body_ = ilist; + sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES); + return *this; +} + +// ---------------------------------------------------------------------------- +// Memory management. + +template +void flat_tree::reserve( + size_type new_capacity) { + impl_.body_.reserve(new_capacity); +} + +template +auto flat_tree::capacity() const + -> size_type { + return impl_.body_.capacity(); +} + +template +void flat_tree::shrink_to_fit() { + impl_.body_.shrink_to_fit(); +} + +// ---------------------------------------------------------------------------- +// Size management. + +template +void flat_tree::clear() { + impl_.body_.clear(); +} + +template +auto flat_tree::size() const + -> size_type { + return impl_.body_.size(); +} + +template +auto flat_tree::max_size() const + -> size_type { + return impl_.body_.max_size(); +} + +template +bool flat_tree::empty() const { + return impl_.body_.empty(); +} + +// ---------------------------------------------------------------------------- +// Iterators. + +template +auto flat_tree::begin() -> iterator { + return impl_.body_.begin(); +} + +template +auto flat_tree::begin() const + -> const_iterator { + return impl_.body_.begin(); +} + +template +auto flat_tree::cbegin() const + -> const_iterator { + return impl_.body_.cbegin(); +} + +template +auto flat_tree::end() -> iterator { + return impl_.body_.end(); +} + +template +auto flat_tree::end() const + -> const_iterator { + return impl_.body_.end(); +} + +template +auto flat_tree::cend() const + -> const_iterator { + return impl_.body_.cend(); +} + +template +auto flat_tree::rbegin() + -> reverse_iterator { + return impl_.body_.rbegin(); +} + +template +auto flat_tree::rbegin() const + -> const_reverse_iterator { + return impl_.body_.rbegin(); +} + +template +auto flat_tree::crbegin() const + -> const_reverse_iterator { + return impl_.body_.crbegin(); +} + +template +auto flat_tree::rend() + -> reverse_iterator { + return impl_.body_.rend(); +} + +template +auto flat_tree::rend() const + -> const_reverse_iterator { + return impl_.body_.rend(); +} + +template +auto flat_tree::crend() const + -> const_reverse_iterator { + return impl_.body_.crend(); +} + +// ---------------------------------------------------------------------------- +// Insert operations. +// +// Currently we use position_hint the same way as eastl or boost: +// https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set.h#L493 + +template +auto flat_tree::insert( + const value_type& val) -> std::pair { + return emplace_key_args(GetKeyFromValue()(val), val); +} + +template +auto flat_tree::insert( + value_type&& val) -> std::pair { + return emplace_key_args(GetKeyFromValue()(val), std::move(val)); +} + +template +auto flat_tree::insert( + const_iterator position_hint, + const value_type& val) -> iterator { + return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), val) + .first; +} + +template +auto flat_tree::insert( + const_iterator position_hint, + value_type&& val) -> iterator { + return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), + std::move(val)) + .first; +} + +template +template +void flat_tree::insert( + InputIterator first, + InputIterator last, + FlatContainerDupes dupes) { + if (first == last) + return; + + // Cache results whether existing elements should be overwritten and whether + // inserting new elements happens immediately or will be done in a batch. + const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES; + const bool insert_inplace = + is_multipass() && std::next(first) == last; + + if (insert_inplace) { + if (overwrite_existing) { + for (; first != last; ++first) + insert_or_assign(*first); + } else + std::copy(first, last, std::inserter(*this, end())); + return; + } + + // Provide a convenience lambda to obtain an iterator pointing past the last + // old element. This needs to be dymanic due to possible re-allocations. + const size_type original_size = size(); + auto middle = [this, original_size]() { + return std::next(begin(), original_size); + }; + + // For batch updates initialize the first insertion point. + difference_type pos_first_new = original_size; + + // Loop over the input range while appending new values and overwriting + // existing ones, if applicable. Keep track of the first insertion point. + if (overwrite_existing) { + for (; first != last; ++first) { + std::pair result = + append_or_assign(begin(), middle(), *first); + if (result.second) { + pos_first_new = + std::min(pos_first_new, std::distance(begin(), result.first)); + } + } + } else { + for (; first != last; ++first) { + std::pair result = + append_unique(begin(), middle(), *first); + if (result.second) { + pos_first_new = + std::min(pos_first_new, std::distance(begin(), result.first)); + } + } + } + + // The new elements might be unordered and contain duplicates, so post-process + // the just inserted elements and merge them with the rest, inserting them at + // the previously found spot. + sort_and_unique(middle(), end(), dupes); + std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(), + value_comp()); +} + +template +template +auto flat_tree::emplace(Args&&... args) + -> std::pair { + return insert(value_type(std::forward(args)...)); +} + +template +template +auto flat_tree::emplace_hint( + const_iterator position_hint, + Args&&... args) -> iterator { + return insert(position_hint, value_type(std::forward(args)...)); +} + +// ---------------------------------------------------------------------------- +// Erase operations. + +template +auto flat_tree::erase( + iterator position) -> iterator { + return impl_.body_.erase(position); +} + +template +auto flat_tree::erase( + const_iterator position) -> iterator { + return impl_.body_.erase(position); +} + +template +template +auto flat_tree::erase(const K& val) + -> size_type { + auto eq_range = equal_range(val); + auto res = std::distance(eq_range.first, eq_range.second); + erase(eq_range.first, eq_range.second); + return res; +} + +template +auto flat_tree::erase( + const_iterator first, + const_iterator last) -> iterator { + return impl_.body_.erase(first, last); +} + +// ---------------------------------------------------------------------------- +// Comparators. + +template +auto flat_tree::key_comp() const + -> key_compare { + return impl_.get_key_comp(); +} + +template +auto flat_tree::value_comp() const + -> value_compare { + return impl_.get_value_comp(); +} + +// ---------------------------------------------------------------------------- +// Search operations. + +template +template +auto flat_tree::count( + const K& key) const -> size_type { + auto eq_range = equal_range(key); + return std::distance(eq_range.first, eq_range.second); +} + +template +template +auto flat_tree::find(const K& key) + -> iterator { + return const_cast_it(as_const().find(key)); +} + +template +template +auto flat_tree::find( + const K& key) const -> const_iterator { + auto eq_range = equal_range(key); + return (eq_range.first == eq_range.second) ? end() : eq_range.first; +} + +template +template +auto flat_tree::equal_range( + const K& key) -> std::pair { + auto res = as_const().equal_range(key); + return {const_cast_it(res.first), const_cast_it(res.second)}; +} + +template +template +auto flat_tree::equal_range( + const K& key) const -> std::pair { + auto lower = lower_bound(key); + + GetKeyFromValue extractor; + if (lower == end() || impl_.get_key_comp()(key, extractor(*lower))) + return {lower, lower}; + + return {lower, std::next(lower)}; +} + +template +template +auto flat_tree::lower_bound( + const K& key) -> iterator { + return const_cast_it(as_const().lower_bound(key)); +} + +template +template +auto flat_tree::lower_bound( + const K& key) const -> const_iterator { + static_assert(std::is_convertible&, const K&>::value, + "Requested type cannot be bound to the container's key_type " + "which is required for a non-transparent compare."); + + const KeyTypeOrK& key_ref = key; + + KeyValueCompare key_value(impl_.get_key_comp()); + return std::lower_bound(begin(), end(), key_ref, key_value); +} + +template +template +auto flat_tree::upper_bound( + const K& key) -> iterator { + return const_cast_it(as_const().upper_bound(key)); +} + +template +template +auto flat_tree::upper_bound( + const K& key) const -> const_iterator { + static_assert(std::is_convertible&, const K&>::value, + "Requested type cannot be bound to the container's key_type " + "which is required for a non-transparent compare."); + + const KeyTypeOrK& key_ref = key; + + KeyValueCompare key_value(impl_.get_key_comp()); + return std::upper_bound(begin(), end(), key_ref, key_value); +} + +// ---------------------------------------------------------------------------- +// General operations. + +template +void flat_tree::swap( + flat_tree& other) noexcept { + std::swap(impl_, other.impl_); +} + +template +template +auto flat_tree::unsafe_emplace( + const_iterator position, + Args&&... args) -> iterator { + return impl_.body_.emplace(position, std::forward(args)...); +} + +template +template +auto flat_tree::emplace_key_args( + const K& key, + Args&&... args) -> std::pair { + auto lower = lower_bound(key); + if (lower == end() || key_comp()(key, GetKeyFromValue()(*lower))) + return {unsafe_emplace(lower, std::forward(args)...), true}; + return {lower, false}; +} + +template +template +auto flat_tree::emplace_hint_key_args( + const_iterator hint, + const K& key, + Args&&... args) -> std::pair { + GetKeyFromValue extractor; + if ((hint == begin() || key_comp()(extractor(*std::prev(hint)), key))) { + if (hint == end() || key_comp()(key, extractor(*hint))) { + // *(hint - 1) < key < *hint => key did not exist and hint is correct. + return {unsafe_emplace(hint, std::forward(args)...), true}; + } + if (!key_comp()(extractor(*hint), key)) { + // key == *hint => no-op, return correct hint. + return {const_cast_it(hint), false}; + } + } + // hint was not helpful, dispatch to hintless version. + return emplace_key_args(key, std::forward(args)...); +} + +// For containers like sets, the key is the same as the value. This implements +// the GetKeyFromValue template parameter to flat_tree for this case. +template +struct GetKeyFromValueIdentity { + const Key& operator()(const Key& k) const { return k; } +}; + +} // namespace internal + +// ---------------------------------------------------------------------------- +// Free functions. + +// Erases all elements that match predicate. It has O(size) complexity. +template +void EraseIf(base::internal::flat_tree& + container, + Predicate pred) { + container.erase(std::remove_if(container.begin(), container.end(), pred), + container.end()); +} + +} // namespace base + +#endif // BASE_CONTAINERS_FLAT_TREE_H_ diff --git a/src/3rdparty/gn/base/containers/queue.h b/src/3rdparty/gn/base/containers/queue.h new file mode 100644 index 00000000000..2d3b480089f --- /dev/null +++ b/src/3rdparty/gn/base/containers/queue.h @@ -0,0 +1,23 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_QUEUE_H_ +#define BASE_CONTAINERS_QUEUE_H_ + +#include + +#include "base/containers/circular_deque.h" + +namespace base { + +// Provides a definition of base::queue that's like std::queue but uses a +// base::circular_queue instead of std::deque. Since std::queue is just a +// wrapper for an underlying type, we can just provide a typedef for it that +// defaults to the base circular_deque. +template > +using queue = std::queue; + +} // namespace base + +#endif // BASE_CONTAINERS_QUEUE_H_ diff --git a/src/3rdparty/gn/base/containers/span.h b/src/3rdparty/gn/base/containers/span.h new file mode 100644 index 00000000000..c9718619bcc --- /dev/null +++ b/src/3rdparty/gn/base/containers/span.h @@ -0,0 +1,453 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_SPAN_H_ +#define BASE_CONTAINERS_SPAN_H_ + +#include + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/stl_util.h" + +namespace base { + +// [views.constants] +constexpr size_t dynamic_extent = static_cast(-1); + +template +class span; + +namespace internal { + +template +struct IsSpanImpl : std::false_type {}; + +template +struct IsSpanImpl> : std::true_type {}; + +template +using IsSpan = IsSpanImpl>; + +template +struct IsStdArrayImpl : std::false_type {}; + +template +struct IsStdArrayImpl> : std::true_type {}; + +template +using IsStdArray = IsStdArrayImpl>; + +template +using IsCArray = std::is_array>; + +template +using IsLegalDataConversion = std::is_convertible; + +template +using ContainerHasConvertibleData = IsLegalDataConversion< + std::remove_pointer_t()))>, + T>; + +template +using ContainerHasIntegralSize = + std::is_integral()))>; + +template +using EnableIfLegalSpanConversion = + std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) && + IsLegalDataConversion::value>; + +// SFINAE check if Array can be converted to a span. +template +using EnableIfSpanCompatibleArray = + std::enable_if_t<(Extent == dynamic_extent || Extent == N) && + ContainerHasConvertibleData::value>; + +// SFINAE check if Container can be converted to a span. +template +using EnableIfSpanCompatibleContainer = + std::enable_if_t::value && + !internal::IsStdArray::value && + !internal::IsCArray::value && + ContainerHasConvertibleData::value && + ContainerHasIntegralSize::value>; + +} // namespace internal + +// A span is a value type that represents an array of elements of type T. Since +// it only consists of a pointer to memory with an associated size, it is very +// light-weight. It is cheap to construct, copy, move and use spans, so that +// users are encouraged to use it as a pass-by-value parameter. A span does not +// own the underlying memory, so care must be taken to ensure that a span does +// not outlive the backing store. +// +// span is somewhat analogous to StringPiece, but with arbitrary element types, +// allowing mutation if T is non-const. +// +// span is implicitly convertible from C++ arrays, as well as most [1] +// container-like types that provide a data() and size() method (such as +// std::vector). A mutable span can also be implicitly converted to an +// immutable span. +// +// Consider using a span for functions that take a data pointer and size +// parameter: it allows the function to still act on an array-like type, while +// allowing the caller code to be a bit more concise. +// +// For read-only data access pass a span: the caller can supply either +// a span or a span, while the callee will have a read-only view. +// For read-write access a mutable span is required. +// +// Without span: +// Read-Only: +// // std::string HexEncode(const uint8_t* data, size_t size); +// std::vector data_buffer = GenerateData(); +// std::string r = HexEncode(data_buffer.data(), data_buffer.size()); +// +// Mutable: +// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...); +// char str_buffer[100]; +// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14); +// +// With span: +// Read-Only: +// // std::string HexEncode(base::span data); +// std::vector data_buffer = GenerateData(); +// std::string r = HexEncode(data_buffer); +// +// Mutable: +// // ssize_t SafeSNPrintf(base::span, const char* fmt, Args...); +// char str_buffer[100]; +// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14); +// +// Spans with "const" and pointers +// ------------------------------- +// +// Const and pointers can get confusing. Here are vectors of pointers and their +// corresponding spans: +// +// const std::vector => base::span +// std::vector => base::span +// const std::vector => base::span +// +// Differences from the working group proposal +// ------------------------------------------- +// +// https://wg21.link/P0122 is the latest working group proposal, Chromium +// currently implements R7. Differences between the proposal and the +// implementation are documented in subsections below. +// +// Differences from [span.objectrep]: +// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of +// std::byte +// +// Differences in constants and types: +// - index_type is aliased to size_t +// +// Differences from [span.sub]: +// - using size_t instead of ptrdiff_t for indexing +// +// Differences from [span.obs]: +// - using size_t instead of ptrdiff_t to represent size() +// +// Differences from [span.elem]: +// - using size_t instead of ptrdiff_t for indexing +// +// Furthermore, all constructors and methods are marked noexcept due to the lack +// of exceptions in Chromium. +// +// Due to the lack of class template argument deduction guides in C++14 +// appropriate make_span() utility functions are provided. + +// [span], class template span +template +class span { + public: + using element_type = T; + using value_type = std::remove_cv_t; + using index_type = size_t; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + static constexpr index_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + constexpr span() noexcept : data_(nullptr), size_(0) { + static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent"); + } + + constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) { + CHECK(Extent == dynamic_extent || Extent == size); + } + + // Artificially templatized to break ambiguity for span(ptr, 0). + template + constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(begin <= end); + } + + template < + size_t N, + typename = internal::EnableIfSpanCompatibleArray> + constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {} + + template < + size_t N, + typename = internal:: + EnableIfSpanCompatibleArray&, N, T, Extent>> + constexpr span(std::array& array) noexcept + : span(base::data(array), N) {} + + template &, + N, + T, + Extent>> + constexpr span(const std::array& array) noexcept + : span(base::data(array), N) {} + + // Conversion from a container that has compatible base::data() and integral + // base::size(). + template > + constexpr span(Container& container) noexcept + : span(base::data(container), base::size(container)) {} + + template < + typename Container, + typename = internal::EnableIfSpanCompatibleContainer> + span(const Container& container) noexcept + : span(base::data(container), base::size(container)) {} + + constexpr span(const span& other) noexcept = default; + + // Conversions from spans of compatible types and extents: this allows a + // span to be seamlessly used as a span, but not the other way + // around. If extent is not dynamic, OtherExtent has to be equal to Extent. + template < + typename U, + size_t OtherExtent, + typename = + internal::EnableIfLegalSpanConversion> + constexpr span(const span& other) + : span(other.data(), other.size()) {} + + constexpr span& operator=(const span& other) noexcept = default; + ~span() noexcept = default; + + // [span.sub], span subviews + template + constexpr span first() const noexcept { + static_assert(Extent == dynamic_extent || Count <= Extent, + "Count must not exceed Extent"); + CHECK(Extent != dynamic_extent || Count <= size()); + return {data(), Count}; + } + + template + constexpr span last() const noexcept { + static_assert(Extent == dynamic_extent || Count <= Extent, + "Count must not exceed Extent"); + CHECK(Extent != dynamic_extent || Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + constexpr span + subspan() const noexcept { + static_assert(Extent == dynamic_extent || Offset <= Extent, + "Offset must not exceed Extent"); + static_assert(Extent == dynamic_extent || Count == dynamic_extent || + Count <= Extent - Offset, + "Count must not exceed Extent - Offset"); + CHECK(Extent != dynamic_extent || Offset <= size()); + CHECK(Extent != dynamic_extent || Count == dynamic_extent || + Count <= size() - Offset); + return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset}; + } + + constexpr span first(size_t count) const noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(count <= size()); + return {data(), count}; + } + + constexpr span last(size_t count) const noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(count <= size()); + return {data() + (size() - count), count}; + } + + constexpr span subspan(size_t offset, + size_t count = dynamic_extent) const + noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(offset <= size()); + CHECK(count == dynamic_extent || count <= size() - offset); + return {data() + offset, count != dynamic_extent ? count : size() - offset}; + } + + // [span.obs], span observers + constexpr size_t size() const noexcept { return size_; } + constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } + constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + constexpr T& operator[](size_t idx) const noexcept { + // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. + CHECK(idx < size()); + return *(data() + idx); + } + + constexpr T& operator()(size_t idx) const noexcept { + // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. + CHECK(idx < size()); + return *(data() + idx); + } + + constexpr T* data() const noexcept { return data_; } + + // [span.iter], span iterator support + constexpr iterator begin() const noexcept { return data(); } + constexpr iterator end() const noexcept { return data() + size(); } + + constexpr const_iterator cbegin() const noexcept { return begin(); } + constexpr const_iterator cend() const noexcept { return end(); } + + constexpr reverse_iterator rbegin() const noexcept { + return reverse_iterator(end()); + } + constexpr reverse_iterator rend() const noexcept { + return reverse_iterator(begin()); + } + + constexpr const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(cend()); + } + constexpr const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(cbegin()); + } + + private: + T* data_; + size_t size_; +}; + +// span::extent can not be declared inline prior to C++17, hence this +// definition is required. +template +constexpr size_t span::extent; + +// [span.comparison], span comparison operators +// Relational operators. Equality is a element-wise comparison. +template +constexpr bool operator==(span lhs, span rhs) noexcept { + return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()); +} + +template +constexpr bool operator!=(span lhs, span rhs) noexcept { + return !(lhs == rhs); +} + +template +constexpr bool operator<(span lhs, span rhs) noexcept { + return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), + rhs.cend()); +} + +template +constexpr bool operator<=(span lhs, span rhs) noexcept { + return !(rhs < lhs); +} + +template +constexpr bool operator>(span lhs, span rhs) noexcept { + return rhs < lhs; +} + +template +constexpr bool operator>=(span lhs, span rhs) noexcept { + return !(lhs < rhs); +} + +// [span.objectrep], views of object representation +template +span +as_bytes(span s) noexcept { + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value>> +span +as_writable_bytes(span s) noexcept { + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// Type-deducing helpers for constructing a span. +template +constexpr span make_span(T* data, size_t size) noexcept { + return {data, size}; +} + +template +constexpr span make_span(T* begin, T* end) noexcept { + return {begin, end}; +} + +template +constexpr span make_span(T (&array)[N]) noexcept { + return array; +} + +template +constexpr span make_span(std::array& array) noexcept { + return array; +} + +template +constexpr span make_span(const std::array& array) noexcept { + return array; +} + +template > +constexpr span make_span(Container& container) noexcept { + return container; +} + +template < + typename Container, + typename T = const typename Container::value_type, + typename = internal::EnableIfSpanCompatibleContainer> +constexpr span make_span(const Container& container) noexcept { + return container; +} + +template +constexpr span make_span(const span& span) noexcept { + return span; +} + +} // namespace base + +#endif // BASE_CONTAINERS_SPAN_H_ diff --git a/src/3rdparty/gn/base/containers/stack.h b/src/3rdparty/gn/base/containers/stack.h new file mode 100644 index 00000000000..1aaa8793c7c --- /dev/null +++ b/src/3rdparty/gn/base/containers/stack.h @@ -0,0 +1,23 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_STACK_H_ +#define BASE_CONTAINERS_STACK_H_ + +#include + +#include "base/containers/circular_deque.h" + +namespace base { + +// Provides a definition of base::stack that's like std::stack but uses a +// base::circular_queue instead of std::deque. Since std::stack is just a +// wrapper for an underlying type, we can just provide a typedef for it that +// defaults to the base circular_deque. +template > +using stack = std::stack; + +} // namespace base + +#endif // BASE_CONTAINERS_STACK_H_ diff --git a/src/3rdparty/gn/base/containers/vector_buffer.h b/src/3rdparty/gn/base/containers/vector_buffer.h new file mode 100644 index 00000000000..a72c1ed95e8 --- /dev/null +++ b/src/3rdparty/gn/base/containers/vector_buffer.h @@ -0,0 +1,163 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_VECTOR_BUFFERS_H_ +#define BASE_CONTAINERS_VECTOR_BUFFERS_H_ + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/macros.h" + +namespace base { +namespace internal { + +// Internal implementation detail of base/containers. +// +// Implements a vector-like buffer that holds a certain capacity of T. Unlike +// std::vector, VectorBuffer never constructs or destructs its arguments, and +// can't change sizes. But it does implement templates to assist in efficient +// moving and destruction of those items manually. +// +// In particular, the destructor function does not iterate over the items if +// there is no destructor. Moves should be implemented as a memcpy/memmove for +// trivially copyable objects (POD) otherwise, it should be a std::move if +// possible, and as a last resort it falls back to a copy. This behavior is +// similar to std::vector. +// +// No special consideration is done for noexcept move constructors since +// we compile without exceptions. +// +// The current API does not support moving overlapping ranges. +template +class VectorBuffer { + public: + constexpr VectorBuffer() = default; + +#if defined(__clang__) && !defined(__native_client__) + // This constructor converts an uninitialized void* to a T* which triggers + // clang Control Flow Integrity. Since this is as-designed, disable. + __attribute__((no_sanitize("cfi-unrelated-cast", "vptr"))) +#endif + VectorBuffer(size_t count) + : buffer_(reinterpret_cast(malloc(sizeof(T) * count))), + capacity_(count) { + } + VectorBuffer(VectorBuffer&& other) noexcept + : buffer_(other.buffer_), capacity_(other.capacity_) { + other.buffer_ = nullptr; + other.capacity_ = 0; + } + + ~VectorBuffer() { free(buffer_); } + + VectorBuffer& operator=(VectorBuffer&& other) { + free(buffer_); + buffer_ = other.buffer_; + capacity_ = other.capacity_; + + other.buffer_ = nullptr; + other.capacity_ = 0; + return *this; + } + + size_t capacity() const { return capacity_; } + + T& operator[](size_t i) { return buffer_[i]; } + const T& operator[](size_t i) const { return buffer_[i]; } + T* begin() { return buffer_; } + T* end() { return &buffer_[capacity_]; } + + // DestructRange ------------------------------------------------------------ + + // Trivially destructible objects need not have their destructors called. + template ::value, + int>::type = 0> + void DestructRange(T* begin, T* end) {} + + // Non-trivially destructible objects must have their destructors called + // individually. + template ::value, + int>::type = 0> + void DestructRange(T* begin, T* end) { + while (begin != end) { + begin->~T(); + begin++; + } + } + + // MoveRange ---------------------------------------------------------------- + // + // The destructor will be called (as necessary) for all moved types. The + // ranges must not overlap. + // + // The parameters and begin and end (one past the last) of the input buffer, + // and the address of the first element to copy to. There must be sufficient + // room in the destination for all items in the range [begin, end). + + // Trivially copyable types can use memcpy. trivially copyable implies + // that there is a trivial destructor as we don't have to call it. + template ::value, + int>::type = 0> + static void MoveRange(T* from_begin, T* from_end, T* to) { + DCHECK(!RangesOverlap(from_begin, from_end, to)); + memcpy(to, from_begin, (from_end - from_begin) * sizeof(T)); + } + + // Not trivially copyable, but movable: call the move constructor and + // destruct the original. + template ::value && + !base::is_trivially_copyable::value, + int>::type = 0> + static void MoveRange(T* from_begin, T* from_end, T* to) { + DCHECK(!RangesOverlap(from_begin, from_end, to)); + while (from_begin != from_end) { + new (to) T(std::move(*from_begin)); + from_begin->~T(); + from_begin++; + to++; + } + } + + // Not movable, not trivially copyable: call the copy constructor and + // destruct the original. + template ::value && + !base::is_trivially_copyable::value, + int>::type = 0> + static void MoveRange(T* from_begin, T* from_end, T* to) { + DCHECK(!RangesOverlap(from_begin, from_end, to)); + while (from_begin != from_end) { + new (to) T(*from_begin); + from_begin->~T(); + from_begin++; + to++; + } + } + + private: + static bool RangesOverlap(const T* from_begin, + const T* from_end, + const T* to) { + return !(to >= from_end || to + (from_end - from_begin) <= from_begin); + } + + T* buffer_ = nullptr; + size_t capacity_ = 0; + + DISALLOW_COPY_AND_ASSIGN(VectorBuffer); +}; + +} // namespace internal +} // namespace base + +#endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_ diff --git a/src/3rdparty/gn/base/environment.cc b/src/3rdparty/gn/base/environment.cc new file mode 100644 index 00000000000..7f0e5d9d5d1 --- /dev/null +++ b/src/3rdparty/gn/base/environment.cc @@ -0,0 +1,237 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/environment.h" + +#include + +#include + +#include "base/memory/ptr_util.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "util/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#endif + +namespace base { + +namespace { + +class EnvironmentImpl : public Environment { + public: + bool GetVar(StringPiece variable_name, std::string* result) override { + if (GetVarImpl(variable_name, result)) + return true; + + // Some commonly used variable names are uppercase while others + // are lowercase, which is inconsistent. Let's try to be helpful + // and look for a variable name with the reverse case. + // I.e. HTTP_PROXY may be http_proxy for some users/systems. + char first_char = variable_name[0]; + std::string alternate_case_var; + if (IsAsciiLower(first_char)) + alternate_case_var = ToUpperASCII(variable_name); + else if (IsAsciiUpper(first_char)) + alternate_case_var = ToLowerASCII(variable_name); + else + return false; + return GetVarImpl(alternate_case_var, result); + } + + bool SetVar(StringPiece variable_name, + const std::string& new_value) override { + return SetVarImpl(variable_name, new_value); + } + + bool UnSetVar(StringPiece variable_name) override { + return UnSetVarImpl(variable_name); + } + + private: + bool GetVarImpl(StringPiece variable_name, std::string* result) { +#if defined(OS_WIN) + DWORD value_length = + ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr, 0); + if (value_length == 0) + return false; + if (result) { + std::unique_ptr value(new wchar_t[value_length]); + ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), value.get(), + value_length); + *result = WideToUTF8(value.get()); + } + return true; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + const char* env_value = getenv(variable_name.data()); + if (!env_value) + return false; + // Note that the variable may be defined but empty. + if (result) + *result = env_value; + return true; +#endif + } + + bool SetVarImpl(StringPiece variable_name, const std::string& new_value) { +#if defined(OS_WIN) + // On success, a nonzero value is returned. + return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), + UTF8ToWide(new_value).c_str()); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On success, zero is returned. + return !setenv(variable_name.data(), new_value.c_str(), 1); +#endif + } + + bool UnSetVarImpl(StringPiece variable_name) { +#if defined(OS_WIN) + // On success, a nonzero value is returned. + return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On success, zero is returned. + return !unsetenv(variable_name.data()); +#endif + } +}; + +// Parses a null-terminated input string of an environment block. The key is +// placed into the given string, and the total length of the line, including +// the terminating null, is returned. +size_t ParseEnvLine(const NativeEnvironmentString::value_type* input, + NativeEnvironmentString* key) { + // Skip to the equals or end of the string, this is the key. + size_t cur = 0; + while (input[cur] && input[cur] != '=') + cur++; + *key = NativeEnvironmentString(&input[0], cur); + + // Now just skip to the end of the string. + while (input[cur]) + cur++; + return cur + 1; +} + +} // namespace + +namespace env_vars { + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// On Posix systems, this variable contains the location of the user's home +// directory. (e.g, /home/username/). +const char kHome[] = "HOME"; +#endif + +} // namespace env_vars + +Environment::~Environment() = default; + +// static +std::unique_ptr Environment::Create() { + return std::make_unique(); +} + +bool Environment::HasVar(StringPiece variable_name) { + return GetVar(variable_name, nullptr); +} + +#if defined(OS_WIN) + +string16 AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes) { + string16 result; + + // First copy all unmodified values to the output. + size_t cur_env = 0; + string16 key; + while (env[cur_env]) { + const wchar_t* line = &env[cur_env]; + size_t line_length = ParseEnvLine(line, &key); + + // Keep only values not specified in the change vector. + EnvironmentMap::const_iterator found_change = changes.find(key); + if (found_change == changes.end()) + result.append(line, line_length); + + cur_env += line_length; + } + + // Now append all modified and new values. + for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end(); + ++i) { + if (!i->second.empty()) { + result.append(i->first); + result.push_back('='); + result.append(i->second); + result.push_back(0); + } + } + + // An additional null marks the end of the list. We always need a double-null + // in case nothing was added above. + if (result.empty()) + result.push_back(0); + result.push_back(0); + return result; +} + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + +std::unique_ptr AlterEnvironment(const char* const* const env, + const EnvironmentMap& changes) { + std::string value_storage; // Holds concatenated null-terminated strings. + std::vector result_indices; // Line indices into value_storage. + + // First build up all of the unchanged environment strings. These are + // null-terminated of the form "key=value". + std::string key; + for (size_t i = 0; env[i]; i++) { + size_t line_length = ParseEnvLine(env[i], &key); + + // Keep only values not specified in the change vector. + EnvironmentMap::const_iterator found_change = changes.find(key); + if (found_change == changes.end()) { + result_indices.push_back(value_storage.size()); + value_storage.append(env[i], line_length); + } + } + + // Now append all modified and new values. + for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end(); + ++i) { + if (!i->second.empty()) { + result_indices.push_back(value_storage.size()); + value_storage.append(i->first); + value_storage.push_back('='); + value_storage.append(i->second); + value_storage.push_back(0); + } + } + + size_t pointer_count_required = + result_indices.size() + 1 + // Null-terminated array of pointers. + (value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer. + std::unique_ptr result(new char*[pointer_count_required]); + + // The string storage goes after the array of pointers. + char* storage_data = + reinterpret_cast(&result.get()[result_indices.size() + 1]); + if (!value_storage.empty()) + memcpy(storage_data, value_storage.data(), value_storage.size()); + + // Fill array of pointers at the beginning of the result. + for (size_t i = 0; i < result_indices.size(); i++) + result[i] = &storage_data[result_indices[i]]; + result[result_indices.size()] = 0; // Null terminator. + + return result; +} + +#endif // OS_WIN + +} // namespace base diff --git a/src/3rdparty/gn/base/environment.h b/src/3rdparty/gn/base/environment.h new file mode 100644 index 00000000000..82af9878dac --- /dev/null +++ b/src/3rdparty/gn/base/environment.h @@ -0,0 +1,86 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ENVIRONMENT_H_ +#define BASE_ENVIRONMENT_H_ + +#include +#include +#include + +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "util/build_config.h" + +namespace base { + +namespace env_vars { + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +extern const char kHome[]; +#endif + +} // namespace env_vars + +class Environment { + public: + virtual ~Environment(); + + // Returns the appropriate platform-specific instance. + static std::unique_ptr Create(); + + // Gets an environment variable's value and stores it in |result|. + // Returns false if the key is unset. + virtual bool GetVar(StringPiece variable_name, std::string* result) = 0; + + // Syntactic sugar for GetVar(variable_name, nullptr); + virtual bool HasVar(StringPiece variable_name); + + // Returns true on success, otherwise returns false. + virtual bool SetVar(StringPiece variable_name, + const std::string& new_value) = 0; + + // Returns true on success, otherwise returns false. + virtual bool UnSetVar(StringPiece variable_name) = 0; +}; + +#if defined(OS_WIN) + +typedef string16 NativeEnvironmentString; +typedef std::map + EnvironmentMap; + +// Returns a modified environment vector constructed from the given environment +// and the list of changes given in |changes|. Each key in the environment is +// matched against the first element of the pairs. In the event of a match, the +// value is replaced by the second of the pair, unless the second is empty, in +// which case the key-value is removed. +// +// This Windows version takes and returns a Windows-style environment block +// which is a concatenated list of null-terminated 16-bit strings. The end is +// marked by a double-null terminator. The size of the returned string will +// include the terminators. +string16 AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes); + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + +typedef std::string NativeEnvironmentString; +typedef std::map + EnvironmentMap; + +// See general comments for the Windows version above. +// +// This Posix version takes and returns a Posix-style environment block, which +// is a null-terminated list of pointers to null-terminated strings. The +// returned array will have appended to it the storage for the array itself so +// there is only one pointer to manage, but this means that you can't copy the +// array without keeping the original around. +std::unique_ptr AlterEnvironment(const char* const* env, + const EnvironmentMap& changes); + +#endif + +} // namespace base + +#endif // BASE_ENVIRONMENT_H_ diff --git a/src/3rdparty/gn/base/files/file.cc b/src/3rdparty/gn/base/files/file.cc new file mode 100644 index 00000000000..98fa3a6597c --- /dev/null +++ b/src/3rdparty/gn/base/files/file.cc @@ -0,0 +1,132 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "util/build_config.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#endif + +namespace base { + +File::Info::Info() : size(0), is_directory(false), is_symbolic_link(false) {} + +File::Info::~Info() = default; + +File::File() + : error_details_(FILE_ERROR_FAILED), created_(false), async_(false) {} + +#if !defined(OS_NACL) +File::File(const FilePath& path, uint32_t flags) + : error_details_(FILE_OK), created_(false), async_(false) { + Initialize(path, flags); +} +#endif + +File::File(PlatformFile platform_file) + : file_(platform_file), + error_details_(FILE_OK), + created_(false), + async_(false) { +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + DCHECK_GE(platform_file, -1); +#endif +} + +File::File(Error error_details) + : error_details_(error_details), created_(false), async_(false) {} + +File::File(File&& other) + : file_(other.TakePlatformFile()), + error_details_(other.error_details()), + created_(other.created()), + async_(other.async_) {} + +File::~File() { + // Go through the AssertIOAllowed logic. + Close(); +} + +// static +File File::CreateForAsyncHandle(PlatformFile platform_file) { + File file(platform_file); + // It would be nice if we could validate that |platform_file| was opened with + // FILE_FLAG_OVERLAPPED on Windows but this doesn't appear to be possible. + file.async_ = true; + return file; +} + +File& File::operator=(File&& other) { + Close(); + SetPlatformFile(other.TakePlatformFile()); + error_details_ = other.error_details(); + created_ = other.created(); + async_ = other.async_; + return *this; +} + +#if !defined(OS_NACL) +void File::Initialize(const FilePath& path, uint32_t flags) { + if (path.ReferencesParent()) { +#if defined(OS_WIN) + ::SetLastError(ERROR_ACCESS_DENIED); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + errno = EACCES; +#else +#error Unsupported platform +#endif + error_details_ = FILE_ERROR_ACCESS_DENIED; + return; + } + DoInitialize(path, flags); +} +#endif + +std::string File::ErrorToString(Error error) { + switch (error) { + case FILE_OK: + return "FILE_OK"; + case FILE_ERROR_FAILED: + return "FILE_ERROR_FAILED"; + case FILE_ERROR_IN_USE: + return "FILE_ERROR_IN_USE"; + case FILE_ERROR_EXISTS: + return "FILE_ERROR_EXISTS"; + case FILE_ERROR_NOT_FOUND: + return "FILE_ERROR_NOT_FOUND"; + case FILE_ERROR_ACCESS_DENIED: + return "FILE_ERROR_ACCESS_DENIED"; + case FILE_ERROR_TOO_MANY_OPENED: + return "FILE_ERROR_TOO_MANY_OPENED"; + case FILE_ERROR_NO_MEMORY: + return "FILE_ERROR_NO_MEMORY"; + case FILE_ERROR_NO_SPACE: + return "FILE_ERROR_NO_SPACE"; + case FILE_ERROR_NOT_A_DIRECTORY: + return "FILE_ERROR_NOT_A_DIRECTORY"; + case FILE_ERROR_INVALID_OPERATION: + return "FILE_ERROR_INVALID_OPERATION"; + case FILE_ERROR_SECURITY: + return "FILE_ERROR_SECURITY"; + case FILE_ERROR_ABORT: + return "FILE_ERROR_ABORT"; + case FILE_ERROR_NOT_A_FILE: + return "FILE_ERROR_NOT_A_FILE"; + case FILE_ERROR_NOT_EMPTY: + return "FILE_ERROR_NOT_EMPTY"; + case FILE_ERROR_INVALID_URL: + return "FILE_ERROR_INVALID_URL"; + case FILE_ERROR_IO: + return "FILE_ERROR_IO"; + case FILE_ERROR_MAX: + break; + } + + NOTREACHED(); + return ""; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file.h b/src/3rdparty/gn/base/files/file.h new file mode 100644 index 00000000000..61239adb8e7 --- /dev/null +++ b/src/3rdparty/gn/base/files/file.h @@ -0,0 +1,356 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_FILE_H_ +#define BASE_FILES_FILE_H_ + +#include + +#include + +#include "base/files/file_path.h" +#include "base/files/platform_file.h" +#include "base/files/scoped_file.h" +#include "base/macros.h" +#include "util/build_config.h" +#include "util/ticks.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#endif + +namespace base { + +#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ + defined(OS_ANDROID) && __ANDROID_API__ < 21 +typedef struct stat stat_wrapper_t; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +typedef struct stat64 stat_wrapper_t; +#endif + +// Thin wrapper around an OS-level file. +// Note that this class does not provide any support for asynchronous IO, other +// than the ability to create asynchronous handles on Windows. +// +// Note about const: this class does not attempt to determine if the underlying +// file system object is affected by a particular method in order to consider +// that method const or not. Only methods that deal with member variables in an +// obvious non-modifying way are marked as const. Any method that forward calls +// to the OS is not considered const, even if there is no apparent change to +// member variables. +class File { + public: + // FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one + // of the five (possibly combining with other flags) when opening or creating + // a file. + // FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior + // will be consistent with O_APPEND on POSIX. + // FLAG_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file on + // creation on POSIX; for existing files, consider using Lock(). + enum Flags { + FLAG_OPEN = 1 << 0, // Opens a file, only if it exists. + FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not + // already exist. + FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file. + FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file. + FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it + // exists. + FLAG_READ = 1 << 5, + FLAG_WRITE = 1 << 6, + FLAG_APPEND = 1 << 7, + FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE. + FLAG_EXCLUSIVE_WRITE = 1 << 9, + FLAG_ASYNC = 1 << 10, + FLAG_TEMPORARY = 1 << 11, // Used on Windows only. + FLAG_HIDDEN = 1 << 12, // Used on Windows only. + FLAG_DELETE_ON_CLOSE = 1 << 13, + FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only. + FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only. + FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags. + FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only. + FLAG_EXECUTE = 1 << 18, // Used on Windows only. + FLAG_SEQUENTIAL_SCAN = 1 << 19, // Used on Windows only. + FLAG_CAN_DELETE_ON_CLOSE = 1 << 20, // Requests permission to delete a file + // via DeleteOnClose() (Windows only). + // See DeleteOnClose() for details. + }; + + // This enum has been recorded in multiple histograms using PlatformFileError + // enum. If the order of the fields needs to change, please ensure that those + // histograms are obsolete or have been moved to a different enum. + // + // FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a + // filesystem restriction. FILE_ERROR_SECURITY is returned when a browser + // policy doesn't allow the operation to be executed. + enum Error { + FILE_OK = 0, + FILE_ERROR_FAILED = -1, + FILE_ERROR_IN_USE = -2, + FILE_ERROR_EXISTS = -3, + FILE_ERROR_NOT_FOUND = -4, + FILE_ERROR_ACCESS_DENIED = -5, + FILE_ERROR_TOO_MANY_OPENED = -6, + FILE_ERROR_NO_MEMORY = -7, + FILE_ERROR_NO_SPACE = -8, + FILE_ERROR_NOT_A_DIRECTORY = -9, + FILE_ERROR_INVALID_OPERATION = -10, + FILE_ERROR_SECURITY = -11, + FILE_ERROR_ABORT = -12, + FILE_ERROR_NOT_A_FILE = -13, + FILE_ERROR_NOT_EMPTY = -14, + FILE_ERROR_INVALID_URL = -15, + FILE_ERROR_IO = -16, + // Put new entries here and increment FILE_ERROR_MAX. + FILE_ERROR_MAX = -17 + }; + + // This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux. + enum Whence { FROM_BEGIN = 0, FROM_CURRENT = 1, FROM_END = 2 }; + + // Used to hold information about a given file. + // If you add more fields to this structure (platform-specific fields are OK), + // make sure to update all functions that use it in file_util_{win|posix}.cc, + // too, and the ParamTraits implementation in + // ipc/ipc_message_utils.cc. + struct Info { + Info(); + ~Info(); +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + // Fills this struct with values from |stat_info|. + void FromStat(const stat_wrapper_t& stat_info); +#endif + + // The size of the file in bytes. Undefined when is_directory is true. + int64_t size; + + // True if the file corresponds to a directory. + bool is_directory; + + // True if the file corresponds to a symbolic link. For Windows currently + // not supported and thus always false. + bool is_symbolic_link; + + // The last modified time of a file. + Ticks last_modified; + + // The last accessed time of a file. + Ticks last_accessed; + + // The creation time of a file. + Ticks creation_time; + }; + + File(); + + // Creates or opens the given file. This will fail with 'access denied' if the + // |path| contains path traversal ('..') components. + File(const FilePath& path, uint32_t flags); + + // Takes ownership of |platform_file|. + explicit File(PlatformFile platform_file); + + // Creates an object with a specific error_details code. + explicit File(Error error_details); + + File(File&& other); + + ~File(); + + // Takes ownership of |platform_file|. + static File CreateForAsyncHandle(PlatformFile platform_file); + + File& operator=(File&& other); + + // Creates or opens the given file. + void Initialize(const FilePath& path, uint32_t flags); + + // Returns |true| if the handle / fd wrapped by this object is valid. This + // method doesn't interact with the file system (and is safe to be called from + // ThreadRestrictions::SetIOAllowed(false) threads). + bool IsValid() const; + + // Returns true if a new file was created (or an old one truncated to zero + // length to simulate a new file, which can happen with + // FLAG_CREATE_ALWAYS), and false otherwise. + bool created() const { return created_; } + + // Returns the OS result of opening this file. Note that the way to verify + // the success of the operation is to use IsValid(), not this method: + // File file(path, flags); + // if (!file.IsValid()) + // return; + Error error_details() const { return error_details_; } + + PlatformFile GetPlatformFile() const; + PlatformFile TakePlatformFile(); + + // Destroying this object closes the file automatically. + void Close(); + + // Changes current position in the file to an |offset| relative to an origin + // defined by |whence|. Returns the resultant current position in the file + // (relative to the start) or -1 in case of error. + int64_t Seek(Whence whence, int64_t offset); + + // Reads the given number of bytes (or until EOF is reached) starting with the + // given offset. Returns the number of bytes read, or -1 on error. Note that + // this function makes a best effort to read all data on all platforms, so it + // is not intended for stream oriented files but instead for cases when the + // normal expectation is that actually |size| bytes are read unless there is + // an error. + int Read(int64_t offset, char* data, int size); + + // Same as above but without seek. + int ReadAtCurrentPos(char* data, int size); + + // Reads the given number of bytes (or until EOF is reached) starting with the + // given offset, but does not make any effort to read all data on all + // platforms. Returns the number of bytes read, or -1 on error. + int ReadNoBestEffort(int64_t offset, char* data, int size); + + // Same as above but without seek. + int ReadAtCurrentPosNoBestEffort(char* data, int size); + + // Writes the given buffer into the file at the given offset, overwritting any + // data that was previously there. Returns the number of bytes written, or -1 + // on error. Note that this function makes a best effort to write all data on + // all platforms. |data| can be nullptr when |size| is 0. + // Ignores the offset and writes to the end of the file if the file was opened + // with FLAG_APPEND. + int Write(int64_t offset, const char* data, int size); + + // Save as above but without seek. + int WriteAtCurrentPos(const char* data, int size); + + // Save as above but does not make any effort to write all data on all + // platforms. Returns the number of bytes written, or -1 on error. + int WriteAtCurrentPosNoBestEffort(const char* data, int size); + + // Returns the current size of this file, or a negative number on failure. + int64_t GetLength(); + + // Truncates the file to the given length. If |length| is greater than the + // current size of the file, the file is extended with zeros. If the file + // doesn't exist, |false| is returned. + bool SetLength(int64_t length); + + // Instructs the filesystem to flush the file to disk. (POSIX: fsync, Windows: + // FlushFileBuffers). + // Calling Flush() does not guarantee file integrity and thus is not a valid + // substitute for file integrity checks and recovery codepaths for malformed + // files. It can also be *really* slow, so avoid blocking on Flush(), + // especially please don't block shutdown on Flush(). + // Latency percentiles of Flush() across all platforms as of July 2016: + // 50 % > 5 ms + // 10 % > 58 ms + // 1 % > 357 ms + // 0.1 % > 1.8 seconds + // 0.01 % > 7.6 seconds + bool Flush(); + + // Returns some basic information for the given file. + bool GetInfo(Info* info); + +#if !defined(OS_FUCHSIA) // Fuchsia's POSIX API does not support file locking. + + // Attempts to take an exclusive write lock on the file. Returns immediately + // (i.e. does not wait for another process to unlock the file). If the lock + // was obtained, the result will be FILE_OK. A lock only guarantees + // that other processes may not also take a lock on the same file with the + // same API - it may still be opened, renamed, unlinked, etc. + // + // Common semantics: + // * Locks are held by processes, but not inherited by child processes. + // * Locks are released by the OS on file close or process termination. + // * Locks are reliable only on local filesystems. + // * Duplicated file handles may also write to locked files. + // Windows-specific semantics: + // * Locks are mandatory for read/write APIs, advisory for mapping APIs. + // * Within a process, locking the same file (by the same or new handle) + // will fail. + // POSIX-specific semantics: + // * Locks are advisory only. + // * Within a process, locking the same file (by the same or new handle) + // will succeed. + // * Closing any descriptor on a given file releases the lock. + Error Lock(); + + // Unlock a file previously locked. + Error Unlock(); + +#endif // !defined(OS_FUCHSIA) + + // Returns a new object referencing this file for use within the current + // process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File + // object that was created or initialized with this flag will have unlinked + // the underlying file when it was created or opened. On Windows, the + // underlying file is deleted when the last handle to it is closed. + File Duplicate() const; + + bool async() const { return async_; } + +#if defined(OS_WIN) + // Sets or clears the DeleteFile disposition on the handle. Returns true if + // the disposition was set or cleared, as indicated by |delete_on_close|. + // + // Microsoft Windows deletes a file only when the last handle to the + // underlying kernel object is closed when the DeleteFile disposition has been + // set by any handle holder. This disposition is be set by: + // - Calling the Win32 DeleteFile function with the path to a file. + // - Opening/creating a file with FLAG_DELETE_ON_CLOSE. + // - Opening/creating a file with FLAG_CAN_DELETE_ON_CLOSE and subsequently + // calling DeleteOnClose(true). + // + // In all cases, all pre-existing handles to the file must have been opened + // with FLAG_SHARE_DELETE. + // + // So: + // - Use FLAG_SHARE_DELETE when creating/opening a file to allow another + // entity on the system to cause it to be deleted when it is closed. (Note: + // another entity can delete the file the moment after it is closed, so not + // using this permission doesn't provide any protections.) + // - Use FLAG_DELETE_ON_CLOSE for any file that is to be deleted after use. + // The OS will ensure it is deleted even in the face of process termination. + // - Use FLAG_CAN_DELETE_ON_CLOSE in conjunction with DeleteOnClose() to alter + // the DeleteFile disposition on an open handle. This fine-grained control + // allows for marking a file for deletion during processing so that it is + // deleted in the event of untimely process termination, and then clearing + // this state once the file is suitable for persistence. + bool DeleteOnClose(bool delete_on_close); +#endif + +#if defined(OS_WIN) + static Error OSErrorToFileError(DWORD last_error); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + static Error OSErrorToFileError(int saved_errno); +#endif + + // Gets the last global error (errno or GetLastError()) and converts it to the + // closest base::File::Error equivalent via OSErrorToFileError(). The returned + // value is only trustworthy immediately after another base::File method + // fails. base::File never resets the global error to zero. + static Error GetLastFileError(); + + // Converts an error value to a human-readable form. Used for logging. + static std::string ErrorToString(Error error); + + private: + // Creates or opens the given file. Only called if |path| has no + // traversal ('..') components. + void DoInitialize(const FilePath& path, uint32_t flags); + + void SetPlatformFile(PlatformFile file); + + ScopedPlatformFile file_; + + Error error_details_; + bool created_; + bool async_; + + DISALLOW_COPY_AND_ASSIGN(File); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_H_ diff --git a/src/3rdparty/gn/base/files/file_enumerator.cc b/src/3rdparty/gn/base/files/file_enumerator.cc new file mode 100644 index 00000000000..9dfb2ba04b5 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_enumerator.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_enumerator.h" + +#include "base/files/file_util.h" + +namespace base { + +FileEnumerator::FileInfo::~FileInfo() = default; + +bool FileEnumerator::ShouldSkip(const FilePath& path) { + FilePath::StringType basename = path.BaseName().value(); + return basename == FILE_PATH_LITERAL(".") || + (basename == FILE_PATH_LITERAL("..") && + !(INCLUDE_DOT_DOT & file_type_)); +} + +bool FileEnumerator::IsTypeMatched(bool is_dir) const { + return (file_type_ & + (is_dir ? FileEnumerator::DIRECTORIES : FileEnumerator::FILES)) != 0; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_enumerator.h b/src/3rdparty/gn/base/files/file_enumerator.h new file mode 100644 index 00000000000..81f757b445a --- /dev/null +++ b/src/3rdparty/gn/base/files/file_enumerator.h @@ -0,0 +1,171 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_FILE_ENUMERATOR_H_ +#define BASE_FILES_FILE_ENUMERATOR_H_ + +#include +#include + +#include + +#include "base/containers/stack.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "util/build_config.h" +#include "util/ticks.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include +#endif + +namespace base { + +// A class for enumerating the files in a provided path. The order of the +// results is not guaranteed. +// +// This is blocking. Do not use on critical threads. +// +// Example: +// +// base::FileEnumerator enum(my_dir, false, base::FileEnumerator::FILES, +// FILE_PATH_LITERAL("*.txt")); +// for (base::FilePath name = enum.Next(); !name.empty(); name = enum.Next()) +// ... +class FileEnumerator { + public: + // Note: copy & assign supported. + class FileInfo { + public: + FileInfo(); + ~FileInfo(); + + bool IsDirectory() const; + + // The name of the file. This will not include any path information. This + // is in constrast to the value returned by FileEnumerator.Next() which + // includes the |root_path| passed into the FileEnumerator constructor. + FilePath GetName() const; + + int64_t GetSize() const; + Ticks GetLastModifiedTime() const; + +#if defined(OS_WIN) + // Note that the cAlternateFileName (used to hold the "short" 8.3 name) + // of the WIN32_FIND_DATA will be empty. Since we don't use short file + // names, we tell Windows to omit it which speeds up the query slightly. + const WIN32_FIND_DATA& find_data() const { return find_data_; } +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + const struct stat& stat() const { return stat_; } +#endif + + private: + friend class FileEnumerator; + +#if defined(OS_WIN) + WIN32_FIND_DATA find_data_; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + struct stat stat_; + FilePath filename_; +#endif + }; + + enum FileType { + FILES = 1 << 0, + DIRECTORIES = 1 << 1, + INCLUDE_DOT_DOT = 1 << 2, +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + SHOW_SYM_LINKS = 1 << 4, +#endif + }; + + // Search policy for intermediate folders. + enum class FolderSearchPolicy { + // Recursive search will pass through folders whose names match the + // pattern. Inside each one, all files will be returned. Folders with names + // that do not match the pattern will be ignored within their interior. + MATCH_ONLY, + // Recursive search will pass through every folder and perform pattern + // matching inside each one. + ALL, + }; + + // |root_path| is the starting directory to search for. It may or may not end + // in a slash. + // + // If |recursive| is true, this will enumerate all matches in any + // subdirectories matched as well. It does a breadth-first search, so all + // files in one directory will be returned before any files in a + // subdirectory. + // + // |file_type|, a bit mask of FileType, specifies whether the enumerator + // should match files, directories, or both. + // + // |pattern| is an optional pattern for which files to match. This + // works like shell globbing. For example, "*.txt" or "Foo???.doc". + // However, be careful in specifying patterns that aren't cross platform + // since the underlying code uses OS-specific matching routines. In general, + // Windows matching is less featureful than others, so test there first. + // If unspecified, this will match all files. + FileEnumerator(const FilePath& root_path, bool recursive, int file_type); + FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern); + FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern, + FolderSearchPolicy folder_search_policy); + ~FileEnumerator(); + + // Returns the next file or an empty string if there are no more results. + // + // The returned path will incorporate the |root_path| passed in the + // constructor: "/file_name.txt". If the |root_path| is absolute, + // then so will be the result of Next(). + FilePath Next(); + + // Write the file info into |info|. + FileInfo GetInfo() const; + + private: + // Returns true if the given path should be skipped in enumeration. + bool ShouldSkip(const FilePath& path); + + bool IsTypeMatched(bool is_dir) const; + + bool IsPatternMatched(const FilePath& src) const; + +#if defined(OS_WIN) + // True when find_data_ is valid. + bool has_find_data_ = false; + WIN32_FIND_DATA find_data_; + HANDLE find_handle_ = INVALID_HANDLE_VALUE; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // The files in the current directory + std::vector directory_entries_; + + // The next entry to use from the directory_entries_ vector + size_t current_directory_entry_; +#endif + FilePath root_path_; + const bool recursive_; + const int file_type_; + FilePath::StringType pattern_; + const FolderSearchPolicy folder_search_policy_; + + // A stack that keeps track of which subdirectories we still need to + // enumerate in the breadth-first search. + base::stack pending_paths_; + + DISALLOW_COPY_AND_ASSIGN(FileEnumerator); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_ENUMERATOR_H_ diff --git a/src/3rdparty/gn/base/files/file_enumerator_posix.cc b/src/3rdparty/gn/base/files/file_enumerator_posix.cc new file mode 100644 index 00000000000..41d52b8afdd --- /dev/null +++ b/src/3rdparty/gn/base/files/file_enumerator_posix.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_enumerator.h" + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "util/build_config.h" + +namespace base { +namespace { + +void GetStat(const FilePath& path, bool show_links, struct stat* st) { + DCHECK(st); + const int res = show_links ? lstat(path.value().c_str(), st) + : stat(path.value().c_str(), st); + if (res < 0) { + // Print the stat() error message unless it was ENOENT and we're following + // symlinks. + if (!(errno == ENOENT && !show_links)) + DPLOG(ERROR) << "Couldn't stat" << path.value(); + memset(st, 0, sizeof(*st)); + } +} + +} // namespace + +// FileEnumerator::FileInfo ---------------------------------------------------- + +FileEnumerator::FileInfo::FileInfo() { + memset(&stat_, 0, sizeof(stat_)); +} + +bool FileEnumerator::FileInfo::IsDirectory() const { + return S_ISDIR(stat_.st_mode); +} + +FilePath FileEnumerator::FileInfo::GetName() const { + return filename_; +} + +int64_t FileEnumerator::FileInfo::GetSize() const { + return stat_.st_size; +} + +Ticks FileEnumerator::FileInfo::GetLastModifiedTime() const { + return stat_.st_mtime; +} + +// FileEnumerator -------------------------------------------------------------- + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type) + : FileEnumerator(root_path, + recursive, + file_type, + FilePath::StringType(), + FolderSearchPolicy::MATCH_ONLY) {} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern) + : FileEnumerator(root_path, + recursive, + file_type, + pattern, + FolderSearchPolicy::MATCH_ONLY) {} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern, + FolderSearchPolicy folder_search_policy) + : current_directory_entry_(0), + root_path_(root_path), + recursive_(recursive), + file_type_(file_type), + pattern_(pattern), + folder_search_policy_(folder_search_policy) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); + + pending_paths_.push(root_path); +} + +FileEnumerator::~FileEnumerator() = default; + +FilePath FileEnumerator::Next() { + ++current_directory_entry_; + + // While we've exhausted the entries in the current directory, do the next + while (current_directory_entry_ >= directory_entries_.size()) { + if (pending_paths_.empty()) + return FilePath(); + + root_path_ = pending_paths_.top(); + root_path_ = root_path_.StripTrailingSeparators(); + pending_paths_.pop(); + + DIR* dir = opendir(root_path_.value().c_str()); + if (!dir) + continue; + + directory_entries_.clear(); + + current_directory_entry_ = 0; + struct dirent* dent; + while ((dent = readdir(dir))) { + FileInfo info; + info.filename_ = FilePath(dent->d_name); + + if (ShouldSkip(info.filename_)) + continue; + + const bool is_pattern_matched = IsPatternMatched(info.filename_); + + // MATCH_ONLY policy enumerates files and directories which matching + // pattern only. So we can early skip further checks. + if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY && + !is_pattern_matched) + continue; + + // Do not call OS stat/lstat if there is no sense to do it. If pattern is + // not matched (file will not appear in results) and search is not + // recursive (possible directory will not be added to pending paths) - + // there is no sense to obtain item below. + if (!recursive_ && !is_pattern_matched) + continue; + + const FilePath full_path = root_path_.Append(info.filename_); + GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_); + + const bool is_dir = info.IsDirectory(); + + if (recursive_ && is_dir) + pending_paths_.push(full_path); + + if (is_pattern_matched && IsTypeMatched(is_dir)) + directory_entries_.push_back(std::move(info)); + } + closedir(dir); + + // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern. + // ALL policy enumerates files in all subfolders by origin pattern. + if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) + pattern_.clear(); + } + + return root_path_.Append( + directory_entries_[current_directory_entry_].filename_); +} + +FileEnumerator::FileInfo FileEnumerator::GetInfo() const { + return directory_entries_[current_directory_entry_]; +} + +bool FileEnumerator::IsPatternMatched(const FilePath& path) const { + return pattern_.empty() || + !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE); +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_enumerator_win.cc b/src/3rdparty/gn/base/files/file_enumerator_win.cc new file mode 100644 index 00000000000..803b7bd4437 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_enumerator_win.cc @@ -0,0 +1,192 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_enumerator.h" + +#include +#include +#include + +#include "base/logging.h" + +namespace base { + +namespace { + +FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy, + const FilePath& root_path, + const FilePath::StringType& pattern) { + // MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy + // collects all files and filters them manually. + switch (policy) { + case FileEnumerator::FolderSearchPolicy::MATCH_ONLY: + return root_path.Append(pattern); + case FileEnumerator::FolderSearchPolicy::ALL: + return root_path.Append(L"*"); + } + NOTREACHED(); + return {}; +} + +} // namespace + +// FileEnumerator::FileInfo ---------------------------------------------------- + +FileEnumerator::FileInfo::FileInfo() { + memset(&find_data_, 0, sizeof(find_data_)); +} + +bool FileEnumerator::FileInfo::IsDirectory() const { + return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +} + +FilePath FileEnumerator::FileInfo::GetName() const { + return FilePath(find_data_.cFileName); +} + +int64_t FileEnumerator::FileInfo::GetSize() const { + ULARGE_INTEGER size; + size.HighPart = find_data_.nFileSizeHigh; + size.LowPart = find_data_.nFileSizeLow; + DCHECK_LE(size.QuadPart, + static_cast(std::numeric_limits::max())); + return static_cast(size.QuadPart); +} + +Ticks FileEnumerator::FileInfo::GetLastModifiedTime() const { + return *reinterpret_cast(&find_data_.ftLastWriteTime); +} + +// FileEnumerator -------------------------------------------------------------- + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type) + : FileEnumerator(root_path, + recursive, + file_type, + FilePath::StringType(), + FolderSearchPolicy::MATCH_ONLY) {} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern) + : FileEnumerator(root_path, + recursive, + file_type, + pattern, + FolderSearchPolicy::MATCH_ONLY) {} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern, + FolderSearchPolicy folder_search_policy) + : recursive_(recursive), + file_type_(file_type), + pattern_(!pattern.empty() ? pattern : L"*"), + folder_search_policy_(folder_search_policy) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); + memset(&find_data_, 0, sizeof(find_data_)); + pending_paths_.push(root_path); +} + +FileEnumerator::~FileEnumerator() { + if (find_handle_ != INVALID_HANDLE_VALUE) + FindClose(find_handle_); +} + +FileEnumerator::FileInfo FileEnumerator::GetInfo() const { + if (!has_find_data_) { + NOTREACHED(); + return FileInfo(); + } + FileInfo ret; + memcpy(&ret.find_data_, &find_data_, sizeof(find_data_)); + return ret; +} + +FilePath FileEnumerator::Next() { + while (has_find_data_ || !pending_paths_.empty()) { + if (!has_find_data_) { + // The last find FindFirstFile operation is done, prepare a new one. + root_path_ = pending_paths_.top(); + pending_paths_.pop(); + + // Start a new find operation. + const FilePath src = + BuildSearchFilter(folder_search_policy_, root_path_, pattern_); + find_handle_ = FindFirstFileEx(src.value().c_str(), + FindExInfoBasic, // Omit short name. + &find_data_, FindExSearchNameMatch, + nullptr, FIND_FIRST_EX_LARGE_FETCH); + has_find_data_ = true; + } else { + // Search for the next file/directory. + if (!FindNextFile(find_handle_, &find_data_)) { + FindClose(find_handle_); + find_handle_ = INVALID_HANDLE_VALUE; + } + } + + if (INVALID_HANDLE_VALUE == find_handle_) { + has_find_data_ = false; + + // MATCH_ONLY policy clears pattern for matched subfolders. ALL policy + // applies pattern for all subfolders. + if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) { + // This is reached when we have finished a directory and are advancing + // to the next one in the queue. We applied the pattern (if any) to the + // files in the root search directory, but for those directories which + // were matched, we want to enumerate all files inside them. This will + // happen when the handle is empty. + pattern_ = L"*"; + } + + continue; + } + + const FilePath filename(find_data_.cFileName); + if (ShouldSkip(filename)) + continue; + + const bool is_dir = + (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + const FilePath abs_path = root_path_.Append(filename); + + // Check if directory should be processed recursive. + if (is_dir && recursive_) { + // If |cur_file| is a directory, and we are doing recursive searching, + // add it to pending_paths_ so we scan it after we finish scanning this + // directory. However, don't do recursion through reparse points or we + // may end up with an infinite cycle. + DWORD attributes = GetFileAttributes(abs_path.value().c_str()); + if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) + pending_paths_.push(abs_path); + } + + if (IsTypeMatched(is_dir) && IsPatternMatched(filename)) + return abs_path; + } + return FilePath(); +} + +bool FileEnumerator::IsPatternMatched(const FilePath& src) const { + switch (folder_search_policy_) { + case FolderSearchPolicy::MATCH_ONLY: + // MATCH_ONLY policy filters by pattern on search request, so all found + // files already fits to pattern. + return true; + case FolderSearchPolicy::ALL: + // ALL policy enumerates all files, we need to check pattern match + // manually. + return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE; + } + NOTREACHED(); + return false; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_path.cc b/src/3rdparty/gn/base/files/file_path.cc new file mode 100644 index 00000000000..014bc9e0cf8 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_path.cc @@ -0,0 +1,669 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_path.h" + +#include +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "util/build_config.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_cftyperef.h" +#include "base/third_party/icu/icu_utf.h" +#endif + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +#include +#endif + +namespace base { + +using StringType = FilePath::StringType; +using StringPieceType = FilePath::StringPieceType; + +namespace { + +const char* const kCommonDoubleExtensionSuffixes[] = {"gz", "z", "bz2", "bz"}; +const char* const kCommonDoubleExtensions[] = {"user.js"}; + +const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0'); + +// If this FilePath contains a drive letter specification, returns the +// position of the last character of the drive letter specification, +// otherwise returns npos. This can only be true on Windows, when a pathname +// begins with a letter followed by a colon. On other platforms, this always +// returns npos. +StringPieceType::size_type FindDriveLetter(StringPieceType path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + // This is dependent on an ASCII-based character set, but that's a + // reasonable assumption. iswalpha can be too inclusive here. + if (path.length() >= 2 && path[1] == L':' && + ((path[0] >= L'A' && path[0] <= L'Z') || + (path[0] >= L'a' && path[0] <= L'z'))) { + return 1; + } +#endif // FILE_PATH_USES_DRIVE_LETTERS + return StringType::npos; +} + +#if defined(FILE_PATH_USES_DRIVE_LETTERS) +bool EqualDriveLetterCaseInsensitive(StringPieceType a, StringPieceType b) { + size_t a_letter_pos = FindDriveLetter(a); + size_t b_letter_pos = FindDriveLetter(b); + + if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos) + return a == b; + + StringPieceType a_letter(a.substr(0, a_letter_pos + 1)); + StringPieceType b_letter(b.substr(0, b_letter_pos + 1)); + if (!StartsWith(a_letter, b_letter, CompareCase::INSENSITIVE_ASCII)) + return false; + + StringPieceType a_rest(a.substr(a_letter_pos + 1)); + StringPieceType b_rest(b.substr(b_letter_pos + 1)); + return a_rest == b_rest; +} +#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) + +bool IsPathAbsolute(StringPieceType path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + StringType::size_type letter = FindDriveLetter(path); + if (letter != StringType::npos) { + // Look for a separator right after the drive specification. + return path.length() > letter + 1 && + FilePath::IsSeparator(path[letter + 1]); + } + // Look for a pair of leading separators. + return path.length() > 1 && FilePath::IsSeparator(path[0]) && + FilePath::IsSeparator(path[1]); +#else // FILE_PATH_USES_DRIVE_LETTERS + // Look for a separator in the first position. + return path.length() > 0 && FilePath::IsSeparator(path[0]); +#endif // FILE_PATH_USES_DRIVE_LETTERS +} + +bool AreAllSeparators(const StringType& input) { + for (StringType::const_iterator it = input.begin(); it != input.end(); ++it) { + if (!FilePath::IsSeparator(*it)) + return false; + } + + return true; +} + +// Find the position of the '.' that separates the extension from the rest +// of the file name. The position is relative to BaseName(), not value(). +// Returns npos if it can't find an extension. +StringType::size_type FinalExtensionSeparatorPosition(const StringType& path) { + // Special case "." and ".." + if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory) + return StringType::npos; + + return path.rfind(FilePath::kExtensionSeparator); +} + +// Same as above, but allow a second extension component of up to 4 +// characters when the rightmost extension component is a common double +// extension (gz, bz2, Z). For example, foo.tar.gz or foo.tar.Z would have +// extension components of '.tar.gz' and '.tar.Z' respectively. +StringType::size_type ExtensionSeparatorPosition(const StringType& path) { + const StringType::size_type last_dot = FinalExtensionSeparatorPosition(path); + + // No extension, or the extension is the whole filename. + if (last_dot == StringType::npos || last_dot == 0U) + return last_dot; + + const StringType::size_type penultimate_dot = + path.rfind(FilePath::kExtensionSeparator, last_dot - 1); + const StringType::size_type last_separator = path.find_last_of( + FilePath::kSeparators, last_dot - 1, FilePath::kSeparatorsLength - 1); + + if (penultimate_dot == StringType::npos || + (last_separator != StringType::npos && + penultimate_dot < last_separator)) { + return last_dot; + } + + for (size_t i = 0; i < arraysize(kCommonDoubleExtensions); ++i) { + StringType extension(path, penultimate_dot + 1); + if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i])) + return penultimate_dot; + } + + StringType extension(path, last_dot + 1); + for (size_t i = 0; i < arraysize(kCommonDoubleExtensionSuffixes); ++i) { + if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensionSuffixes[i])) { + if ((last_dot - penultimate_dot) <= 5U && + (last_dot - penultimate_dot) > 1U) { + return penultimate_dot; + } + } + } + + return last_dot; +} + +// Returns true if path is "", ".", or "..". +bool IsEmptyOrSpecialCase(const StringType& path) { + // Special cases "", ".", and ".." + if (path.empty() || path == FilePath::kCurrentDirectory || + path == FilePath::kParentDirectory) { + return true; + } + + return false; +} + +} // namespace + +FilePath::FilePath() = default; + +FilePath::FilePath(const FilePath& that) = default; +FilePath::FilePath(FilePath&& that) noexcept = default; + +FilePath::FilePath(StringPieceType path) { + path.CopyToString(&path_); + StringType::size_type nul_pos = path_.find(kStringTerminator); + if (nul_pos != StringType::npos) + path_.erase(nul_pos, StringType::npos); +} + +FilePath::~FilePath() = default; + +FilePath& FilePath::operator=(const FilePath& that) = default; + +FilePath& FilePath::operator=(FilePath&& that) = default; + +bool FilePath::operator==(const FilePath& that) const { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + return EqualDriveLetterCaseInsensitive(this->path_, that.path_); +#else // defined(FILE_PATH_USES_DRIVE_LETTERS) + return path_ == that.path_; +#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) +} + +bool FilePath::operator!=(const FilePath& that) const { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + return !EqualDriveLetterCaseInsensitive(this->path_, that.path_); +#else // defined(FILE_PATH_USES_DRIVE_LETTERS) + return path_ != that.path_; +#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) +} + +std::ostream& operator<<(std::ostream& out, const FilePath& file_path) { + return out << file_path.value(); +} + +// static +bool FilePath::IsSeparator(CharType character) { + for (size_t i = 0; i < kSeparatorsLength - 1; ++i) { + if (character == kSeparators[i]) { + return true; + } + } + + return false; +} + +void FilePath::GetComponents(std::vector* components) const { + DCHECK(components); + if (!components) + return; + components->clear(); + if (value().empty()) + return; + + std::vector ret_val; + FilePath current = *this; + FilePath base; + + // Capture path components. + while (current != current.DirName()) { + base = current.BaseName(); + if (!AreAllSeparators(base.value())) + ret_val.push_back(base.value()); + current = current.DirName(); + } + + // Capture root, if any. + base = current.BaseName(); + if (!base.value().empty() && base.value() != kCurrentDirectory) + ret_val.push_back(current.BaseName().value()); + + // Capture drive letter, if any. + FilePath dir = current.DirName(); + StringType::size_type letter = FindDriveLetter(dir.value()); + if (letter != StringType::npos) { + ret_val.push_back(StringType(dir.value(), 0, letter + 1)); + } + + *components = std::vector(ret_val.rbegin(), ret_val.rend()); +} + +bool FilePath::IsParent(const FilePath& child) const { + return AppendRelativePath(child, nullptr); +} + +bool FilePath::AppendRelativePath(const FilePath& child, FilePath* path) const { + std::vector parent_components; + std::vector child_components; + GetComponents(&parent_components); + child.GetComponents(&child_components); + + if (parent_components.empty() || + parent_components.size() >= child_components.size()) + return false; + + std::vector::const_iterator parent_comp = + parent_components.begin(); + std::vector::const_iterator child_comp = child_components.begin(); + +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + // Windows can access case sensitive filesystems, so component + // comparisions must be case sensitive, but drive letters are + // never case sensitive. + if ((FindDriveLetter(*parent_comp) != StringType::npos) && + (FindDriveLetter(*child_comp) != StringType::npos)) { + if (!StartsWith(*parent_comp, *child_comp, CompareCase::INSENSITIVE_ASCII)) + return false; + ++parent_comp; + ++child_comp; + } +#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) + + while (parent_comp != parent_components.end()) { + if (*parent_comp != *child_comp) + return false; + ++parent_comp; + ++child_comp; + } + + if (path != nullptr) { + for (; child_comp != child_components.end(); ++child_comp) { + *path = path->Append(*child_comp); + } + } + return true; +} + +// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't +// guaranteed to not modify their input strings, and in fact are implemented +// differently in this regard on different platforms. Don't use them, but +// adhere to their behavior. +FilePath FilePath::DirName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, always needs to remain in the output. If there + // is no drive letter, as will always be the case on platforms which do not + // support drive letters, letter will be npos, or -1, so the comparisons and + // resizes below using letter will still be valid. + StringType::size_type letter = FindDriveLetter(new_path.path_); + + StringType::size_type last_separator = new_path.path_.find_last_of( + kSeparators, StringType::npos, kSeparatorsLength - 1); + if (last_separator == StringType::npos) { + // path_ is in the current directory. + new_path.path_.resize(letter + 1); + } else if (last_separator == letter + 1) { + // path_ is in the root directory. + new_path.path_.resize(letter + 2); + } else if (last_separator == letter + 2 && + IsSeparator(new_path.path_[letter + 1])) { + // path_ is in "//" (possibly with a drive letter); leave the double + // separator intact indicating alternate root. + new_path.path_.resize(letter + 3); + } else if (last_separator != 0) { + // path_ is somewhere else, trim the basename. + new_path.path_.resize(last_separator); + } + + new_path.StripTrailingSeparatorsInternal(); + if (!new_path.path_.length()) + new_path.path_ = kCurrentDirectory; + + return new_path; +} + +FilePath FilePath::BaseName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, is always stripped. + StringType::size_type letter = FindDriveLetter(new_path.path_); + if (letter != StringType::npos) { + new_path.path_.erase(0, letter + 1); + } + + // Keep everything after the final separator, but if the pathname is only + // one character and it's a separator, leave it alone. + StringType::size_type last_separator = new_path.path_.find_last_of( + kSeparators, StringType::npos, kSeparatorsLength - 1); + if (last_separator != StringType::npos && + last_separator < new_path.path_.length() - 1) { + new_path.path_.erase(0, last_separator + 1); + } + + return new_path; +} + +StringType FilePath::Extension() const { + FilePath base(BaseName()); + const StringType::size_type dot = ExtensionSeparatorPosition(base.path_); + if (dot == StringType::npos) + return StringType(); + + return base.path_.substr(dot, StringType::npos); +} + +StringType FilePath::FinalExtension() const { + FilePath base(BaseName()); + const StringType::size_type dot = FinalExtensionSeparatorPosition(base.path_); + if (dot == StringType::npos) + return StringType(); + + return base.path_.substr(dot, StringType::npos); +} + +FilePath FilePath::RemoveExtension() const { + if (Extension().empty()) + return *this; + + const StringType::size_type dot = ExtensionSeparatorPosition(path_); + if (dot == StringType::npos) + return *this; + + return FilePath(path_.substr(0, dot)); +} + +FilePath FilePath::RemoveFinalExtension() const { + if (FinalExtension().empty()) + return *this; + + const StringType::size_type dot = FinalExtensionSeparatorPosition(path_); + if (dot == StringType::npos) + return *this; + + return FilePath(path_.substr(0, dot)); +} + +FilePath FilePath::InsertBeforeExtension(StringPieceType suffix) const { + if (suffix.empty()) + return FilePath(path_); + + if (IsEmptyOrSpecialCase(BaseName().value())) + return FilePath(); + + StringType ext = Extension(); + StringType ret = RemoveExtension().value(); + suffix.AppendToString(&ret); + ret.append(ext); + return FilePath(ret); +} + +FilePath FilePath::InsertBeforeExtensionASCII(StringPiece suffix) const { + DCHECK(IsStringASCII(suffix)); +#if defined(OS_WIN) + return InsertBeforeExtension(ASCIIToUTF16(suffix)); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return InsertBeforeExtension(suffix); +#endif +} + +FilePath FilePath::AddExtension(StringPieceType extension) const { + if (IsEmptyOrSpecialCase(BaseName().value())) + return FilePath(); + + // If the new extension is "" or ".", then just return the current FilePath. + if (extension.empty() || + (extension.size() == 1 && extension[0] == kExtensionSeparator)) + return *this; + + StringType str = path_; + if (extension[0] != kExtensionSeparator && + *(str.end() - 1) != kExtensionSeparator) { + str.append(1, kExtensionSeparator); + } + extension.AppendToString(&str); + return FilePath(str); +} + +FilePath FilePath::ReplaceExtension(StringPieceType extension) const { + if (IsEmptyOrSpecialCase(BaseName().value())) + return FilePath(); + + FilePath no_ext = RemoveExtension(); + // If the new extension is "" or ".", then just remove the current extension. + if (extension.empty() || + (extension.size() == 1 && extension[0] == kExtensionSeparator)) + return no_ext; + + StringType str = no_ext.value(); + if (extension[0] != kExtensionSeparator) + str.append(1, kExtensionSeparator); + extension.AppendToString(&str); + return FilePath(str); +} + +FilePath FilePath::Append(StringPieceType component) const { + StringPieceType appended = component; + StringType without_nuls; + + StringType::size_type nul_pos = component.find(kStringTerminator); + if (nul_pos != StringPieceType::npos) { + component.substr(0, nul_pos).CopyToString(&without_nuls); + appended = StringPieceType(without_nuls); + } + + DCHECK(!IsPathAbsolute(appended)); + + if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) { + // Append normally doesn't do any normalization, but as a special case, + // when appending to kCurrentDirectory, just return a new path for the + // component argument. Appending component to kCurrentDirectory would + // serve no purpose other than needlessly lengthening the path, and + // it's likely in practice to wind up with FilePath objects containing + // only kCurrentDirectory when calling DirName on a single relative path + // component. + return FilePath(appended); + } + + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // Don't append a separator if the path is empty (indicating the current + // directory) or if the path component is empty (indicating nothing to + // append). + if (!appended.empty() && !new_path.path_.empty()) { + // Don't append a separator if the path still ends with a trailing + // separator after stripping (indicating the root directory). + if (!IsSeparator(new_path.path_.back())) { + // Don't append a separator if the path is just a drive letter. + if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { + new_path.path_.append(1, kSeparators[0]); + } + } + } + + appended.AppendToString(&new_path.path_); + return new_path; +} + +FilePath FilePath::Append(const FilePath& component) const { + return Append(component.value()); +} + +FilePath FilePath::AppendASCII(StringPiece component) const { + DCHECK(base::IsStringASCII(component)); +#if defined(OS_WIN) + return Append(ASCIIToUTF16(component)); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return Append(component); +#endif +} + +bool FilePath::IsAbsolute() const { + return IsPathAbsolute(path_); +} + +bool FilePath::EndsWithSeparator() const { + if (empty()) + return false; + return IsSeparator(path_.back()); +} + +FilePath FilePath::AsEndingWithSeparator() const { + if (EndsWithSeparator() || path_.empty()) + return *this; + + StringType path_str; + path_str.reserve(path_.length() + 1); // Only allocate string once. + + path_str = path_; + path_str.append(&kSeparators[0], 1); + return FilePath(path_str); +} + +FilePath FilePath::StripTrailingSeparators() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + return new_path; +} + +bool FilePath::ReferencesParent() const { + if (path_.find(kParentDirectory) == StringType::npos) { + // GetComponents is quite expensive, so avoid calling it in the majority + // of cases where there isn't a kParentDirectory anywhere in the path. + return false; + } + + std::vector components; + GetComponents(&components); + + std::vector::const_iterator it = components.begin(); + for (; it != components.end(); ++it) { + const StringType& component = *it; + // Windows has odd, undocumented behavior with path components containing + // only whitespace and . characters. So, if all we see is . and + // whitespace, then we treat any .. sequence as referencing parent. + // For simplicity we enforce this on all platforms. + if (component.find_first_not_of(FILE_PATH_LITERAL(". \n\r\t")) == + std::string::npos && + component.find(kParentDirectory) != std::string::npos) { + return true; + } + } + return false; +} + +#if defined(OS_WIN) + +string16 FilePath::LossyDisplayName() const { + return path_; +} + +std::string FilePath::MaybeAsASCII() const { + if (base::IsStringASCII(path_)) + return UTF16ToASCII(path_); + return std::string(); +} + +std::string FilePath::AsUTF8Unsafe() const { + return WideToUTF8(value()); +} + +string16 FilePath::AsUTF16Unsafe() const { + return value(); +} + +// static +FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) { + return FilePath(UTF8ToWide(utf8)); +} + +// static +FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { + return FilePath(utf16); +} + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + +// See file_path.h for a discussion of the encoding of paths on POSIX +// platforms. These encoding conversion functions are not quite correct. + +std::string FilePath::MaybeAsASCII() const { + if (base::IsStringASCII(path_)) + return path_; + return std::string(); +} + +std::string FilePath::AsUTF8Unsafe() const { + return value(); +} + +string16 FilePath::AsUTF16Unsafe() const { + return UTF8ToUTF16(value()); +} + +// static +FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) { + return FilePath(utf8); +} + +// static +FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { + return FilePath(UTF16ToUTF8(utf16)); +} + +#endif // defined(OS_WIN) + +void FilePath::StripTrailingSeparatorsInternal() { + // If there is no drive letter, start will be 1, which will prevent stripping + // the leading separator if there is only one separator. If there is a drive + // letter, start will be set appropriately to prevent stripping the first + // separator following the drive letter, if a separator immediately follows + // the drive letter. + StringType::size_type start = FindDriveLetter(path_) + 2; + + StringType::size_type last_stripped = StringType::npos; + for (StringType::size_type pos = path_.length(); + pos > start && IsSeparator(path_[pos - 1]); --pos) { + // If the string only has two separators and they're at the beginning, + // don't strip them, unless the string began with more than two separators. + if (pos != start + 1 || last_stripped == start + 2 || + !IsSeparator(path_[start - 1])) { + path_.resize(pos - 1); + last_stripped = pos; + } + } +} + +FilePath FilePath::NormalizePathSeparators() const { + return NormalizePathSeparatorsTo(kSeparators[0]); +} + +FilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const { +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + DCHECK_NE(kSeparators + kSeparatorsLength, + std::find(kSeparators, kSeparators + kSeparatorsLength, separator)); + StringType copy = path_; + for (size_t i = 0; i < kSeparatorsLength; ++i) { + std::replace(copy.begin(), copy.end(), kSeparators[i], separator); + } + return FilePath(copy); +#else + return *this; +#endif +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_path.h b/src/3rdparty/gn/base/files/file_path.h new file mode 100644 index 00000000000..1717cba713f --- /dev/null +++ b/src/3rdparty/gn/base/files/file_path.h @@ -0,0 +1,426 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FilePath is a container for pathnames stored in a platform's native string +// type, providing containers for manipulation in according with the +// platform's conventions for pathnames. It supports the following path +// types: +// +// POSIX Windows +// --------------- ---------------------------------- +// Fundamental type char[] wchar_t[] +// Encoding unspecified* UTF-16 +// Separator / \, tolerant of / +// Drive letters no case-insensitive A-Z followed by : +// Alternate root // (surprise!) \\, for UNC paths +// +// * The encoding need not be specified on POSIX systems, although some +// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. +// Chrome OS also uses UTF-8. +// Linux does not specify an encoding, but in practice, the locale's +// character set may be used. +// +// For more arcane bits of path trivia, see below. +// +// FilePath objects are intended to be used anywhere paths are. An +// application may pass FilePath objects around internally, masking the +// underlying differences between systems, only differing in implementation +// where interfacing directly with the system. For example, a single +// OpenFile(const FilePath &) function may be made available, allowing all +// callers to operate without regard to the underlying implementation. On +// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might +// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This +// allows each platform to pass pathnames around without requiring conversions +// between encodings, which has an impact on performance, but more imporantly, +// has an impact on correctness on platforms that do not have well-defined +// encodings for pathnames. +// +// Several methods are available to perform common operations on a FilePath +// object, such as determining the parent directory (DirName), isolating the +// final path component (BaseName), and appending a relative pathname string +// to an existing FilePath object (Append). These methods are highly +// recommended over attempting to split and concatenate strings directly. +// These methods are based purely on string manipulation and knowledge of +// platform-specific pathname conventions, and do not consult the filesystem +// at all, making them safe to use without fear of blocking on I/O operations. +// These methods do not function as mutators but instead return distinct +// instances of FilePath objects, and are therefore safe to use on const +// objects. The objects themselves are safe to share between threads. +// +// To aid in initialization of FilePath objects from string literals, a +// FILE_PATH_LITERAL macro is provided, which accounts for the difference +// between char[]-based pathnames on POSIX systems and wchar_t[]-based +// pathnames on Windows. +// +// As a precaution against premature truncation, paths can't contain NULs. +// +// Because a FilePath object should not be instantiated at the global scope, +// instead, use a FilePath::CharType[] and initialize it with +// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the +// character array. Example: +// +// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); +// | +// | void Function() { +// | FilePath log_file_path(kLogFileName); +// | [...] +// | } +// +// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even +// when the UI language is RTL. This means you always need to pass filepaths +// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the +// RTL UI. +// +// This is a very common source of bugs, please try to keep this in mind. +// +// ARCANE BITS OF PATH TRIVIA +// +// - A double leading slash is actually part of the POSIX standard. Systems +// are allowed to treat // as an alternate root, as Windows does for UNC +// (network share) paths. Most POSIX systems don't do anything special +// with two leading slashes, but FilePath handles this case properly +// in case it ever comes across such a system. FilePath needs this support +// for Windows UNC paths, anyway. +// References: +// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname") +// and 4.12 ("Pathname Resolution"), available at: +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 +// +// - Windows treats c:\\ the same way it treats \\. This was intended to +// allow older applications that require drive letters to support UNC paths +// like \\server\share\path, by permitting c:\\server\share\path as an +// equivalent. Since the OS treats these paths specially, FilePath needs +// to do the same. Since Windows can use either / or \ as the separator, +// FilePath treats c://, c:\\, //, and \\ all equivalently. +// Reference: +// The Old New Thing, "Why is a drive letter permitted in front of UNC +// paths (sometimes)?", available at: +// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx + +#ifndef BASE_FILES_FILE_PATH_H_ +#define BASE_FILES_FILE_PATH_H_ + +#include + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "util/build_config.h" + +// Windows-style drive letter support and pathname separator characters can be +// enabled and disabled independently, to aid testing. These #defines are +// here so that the same setting can be used in both the implementation and +// in the unit test. +#if defined(OS_WIN) +#define FILE_PATH_USES_DRIVE_LETTERS +#define FILE_PATH_USES_WIN_SEPARATORS +#endif // OS_WIN + +// To print path names portably use PRIsFP (based on PRIuS and friends from +// C99 and format_macros.h) like this: +// base::StringPrintf("Path is %" PRIsFP ".\n", path.value().c_str()); +#if defined(OS_WIN) +#define PRIsFP "ls" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#define PRIsFP "s" +#endif // OS_WIN + +namespace base { + +class Pickle; +class PickleIterator; + +// An abstraction to isolate users from the differences between native +// pathnames on different platforms. +class FilePath { + public: +#if defined(OS_WIN) + // On Windows, for Unicode-aware applications, native pathnames are wchar_t + // arrays encoded in UTF-16. + typedef std::wstring StringType; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On most platforms, native pathnames are char arrays, and the encoding + // may or may not be specified. On Mac OS X, native pathnames are encoded + // in UTF-8. + typedef std::string StringType; +#endif // OS_WIN + + typedef BasicStringPiece StringPieceType; + typedef StringType::value_type CharType; + + // Null-terminated array of separators used to separate components in + // hierarchical paths. Each character in this array is a valid separator, + // but kSeparators[0] is treated as the canonical separator and will be used + // when composing pathnames. + static const CharType kSeparators[]; + + // arraysize(kSeparators). + static const size_t kSeparatorsLength; + + // A special path component meaning "this directory." + static const CharType kCurrentDirectory[]; + + // A special path component meaning "the parent directory." + static const CharType kParentDirectory[]; + + // The character used to identify a file extension. + static const CharType kExtensionSeparator; + + FilePath(); + FilePath(const FilePath& that); + explicit FilePath(StringPieceType path); + ~FilePath(); + FilePath& operator=(const FilePath& that); + + // Constructs FilePath with the contents of |that|, which is left in valid but + // unspecified state. + FilePath(FilePath&& that) noexcept; + // Replaces the contents with those of |that|, which is left in valid but + // unspecified state. + FilePath& operator=(FilePath&& that); + + bool operator==(const FilePath& that) const; + + bool operator!=(const FilePath& that) const; + + // Required for some STL containers and operations + bool operator<(const FilePath& that) const { return path_ < that.path_; } + + const StringType& value() const { return path_; } + + bool empty() const { return path_.empty(); } + + void clear() { path_.clear(); } + + // Returns true if |character| is in kSeparators. + static bool IsSeparator(CharType character); + + // Returns a vector of all of the components of the provided path. It is + // equivalent to calling DirName().value() on the path's root component, + // and BaseName().value() on each child component. + // + // To make sure this is lossless so we can differentiate absolute and + // relative paths, the root slash will be included even though no other + // slashes will be. The precise behavior is: + // + // Posix: "/foo/bar" -> [ "/", "foo", "bar" ] + // Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ] + void GetComponents(std::vector* components) const; + + // Returns true if this FilePath is a strict parent of the |child|. Absolute + // and relative paths are accepted i.e. is /foo parent to /foo/bar and + // is foo parent to foo/bar. Does not convert paths to absolute, follow + // symlinks or directory navigation (e.g. ".."). A path is *NOT* its own + // parent. + bool IsParent(const FilePath& child) const; + + // If IsParent(child) holds, appends to path (if non-NULL) the + // relative path to child and returns true. For example, if parent + // holds "/Users/johndoe/Library/Application Support", child holds + // "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and + // *path holds "/Users/johndoe/Library/Caches", then after + // parent.AppendRelativePath(child, path) is called *path will hold + // "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise, + // returns false. + bool AppendRelativePath(const FilePath& child, FilePath* path) const; + + // Returns a FilePath corresponding to the directory containing the path + // named by this object, stripping away the file component. If this object + // only contains one component, returns a FilePath identifying + // kCurrentDirectory. If this object already refers to the root directory, + // returns a FilePath identifying the root directory. Please note that this + // doesn't resolve directory navigation, e.g. the result for "../a" is "..". + FilePath DirName() const WARN_UNUSED_RESULT; + + // Returns a FilePath corresponding to the last path component of this + // object, either a file or a directory. If this object already refers to + // the root directory, returns a FilePath identifying the root directory; + // this is the only situation in which BaseName will return an absolute path. + FilePath BaseName() const WARN_UNUSED_RESULT; + + // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if + // the file has no extension. If non-empty, Extension() will always start + // with precisely one ".". The following code should always work regardless + // of the value of path. For common double-extensions like .tar.gz and + // .user.js, this method returns the combined extension. For a single + // component, use FinalExtension(). + // new_path = path.RemoveExtension().value().append(path.Extension()); + // ASSERT(new_path == path.value()); + // NOTE: this is different from the original file_util implementation which + // returned the extension without a leading "." ("jpg" instead of ".jpg") + StringType Extension() const WARN_UNUSED_RESULT; + + // Returns the path's file extension, as in Extension(), but will + // never return a double extension. + // + // TODO(davidben): Check all our extension-sensitive code to see if + // we can rename this to Extension() and the other to something like + // LongExtension(), defaulting to short extensions and leaving the + // long "extensions" to logic like base::GetUniquePathNumber(). + StringType FinalExtension() const WARN_UNUSED_RESULT; + + // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" + // NOTE: this is slightly different from the similar file_util implementation + // which returned simply 'jojo'. + FilePath RemoveExtension() const WARN_UNUSED_RESULT; + + // Removes the path's file extension, as in RemoveExtension(), but + // ignores double extensions. + FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT; + + // Inserts |suffix| after the file name portion of |path| but before the + // extension. Returns "" if BaseName() == "." or "..". + // Examples: + // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg" + // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg" + // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)" + // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)" + FilePath InsertBeforeExtension(StringPieceType suffix) const + WARN_UNUSED_RESULT; + FilePath InsertBeforeExtensionASCII(StringPiece suffix) const + WARN_UNUSED_RESULT; + + // Adds |extension| to |file_name|. Returns the current FilePath if + // |extension| is empty. Returns "" if BaseName() == "." or "..". + FilePath AddExtension(StringPieceType extension) const WARN_UNUSED_RESULT; + + // Replaces the extension of |file_name| with |extension|. If |file_name| + // does not have an extension, then |extension| is added. If |extension| is + // empty, then the extension is removed from |file_name|. + // Returns "" if BaseName() == "." or "..". + FilePath ReplaceExtension(StringPieceType extension) const WARN_UNUSED_RESULT; + + // Returns a FilePath by appending a separator and the supplied path + // component to this object's path. Append takes care to avoid adding + // excessive separators if this object's path already ends with a separator. + // If this object's path is kCurrentDirectory, a new FilePath corresponding + // only to |component| is returned. |component| must be a relative path; + // it is an error to pass an absolute path. + FilePath Append(StringPieceType component) const WARN_UNUSED_RESULT; + FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT; + + // Although Windows StringType is std::wstring, since the encoding it uses for + // paths is well defined, it can handle ASCII path components as well. + // Mac uses UTF8, and since ASCII is a subset of that, it works there as well. + // On Linux, although it can use any 8-bit encoding for paths, we assume that + // ASCII is a valid subset, regardless of the encoding, since many operating + // system paths will always be ASCII. + FilePath AppendASCII(StringPiece component) const WARN_UNUSED_RESULT; + + // Returns true if this FilePath contains an absolute path. On Windows, an + // absolute path begins with either a drive letter specification followed by + // a separator character, or with two separator characters. On POSIX + // platforms, an absolute path begins with a separator character. + bool IsAbsolute() const; + + // Returns true if the patch ends with a path separator character. + bool EndsWithSeparator() const WARN_UNUSED_RESULT; + + // Returns a copy of this FilePath that ends with a trailing separator. If + // the input path is empty, an empty FilePath will be returned. + FilePath AsEndingWithSeparator() const WARN_UNUSED_RESULT; + + // Returns a copy of this FilePath that does not end with a trailing + // separator. + FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT; + + // Returns true if this FilePath contains an attempt to reference a parent + // directory (e.g. has a path component that is ".."). + bool ReferencesParent() const; + + // Return a Unicode human-readable version of this path. + // Warning: you can *not*, in general, go from a display name back to a real + // path. Only use this when displaying paths to users, not just when you + // want to stuff a string16 into some other API. + string16 LossyDisplayName() const; + + // Return the path as ASCII, or the empty string if the path is not ASCII. + // This should only be used for cases where the FilePath is representing a + // known-ASCII filename. + std::string MaybeAsASCII() const; + + // Return the path as UTF-8. + // + // This function is *unsafe* as there is no way to tell what encoding is + // used in file names on POSIX systems other than Mac and Chrome OS, + // although UTF-8 is practically used everywhere these days. To mitigate + // the encoding issue, this function internally calls + // SysNativeMBToWide() on POSIX systems other than Mac and Chrome OS, + // per assumption that the current locale's encoding is used in file + // names, but this isn't a perfect solution. + // + // Once it becomes safe to to stop caring about non-UTF-8 file names, + // the SysNativeMBToWide() hack will be removed from the code, along + // with "Unsafe" in the function name. + std::string AsUTF8Unsafe() const; + + // Similar to AsUTF8Unsafe, but returns UTF-16 instead. + string16 AsUTF16Unsafe() const; + + // Returns a FilePath object from a path name in UTF-8. This function + // should only be used for cases where you are sure that the input + // string is UTF-8. + // + // Like AsUTF8Unsafe(), this function is unsafe. This function + // internally calls SysWideToNativeMB() on POSIX systems other than Mac + // and Chrome OS, to mitigate the encoding issue. See the comment at + // AsUTF8Unsafe() for details. + static FilePath FromUTF8Unsafe(StringPiece utf8); + + // Similar to FromUTF8Unsafe, but accepts UTF-16 instead. + static FilePath FromUTF16Unsafe(StringPiece16 utf16); + + // Normalize all path separators to backslash on Windows + // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems. + FilePath NormalizePathSeparators() const; + + // Normalize all path separattors to given type on Windows + // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems. + FilePath NormalizePathSeparatorsTo(CharType separator) const; + + private: + // Remove trailing separators from this object. If the path is absolute, it + // will never be stripped any more than to refer to the absolute root + // directory, so "////" will become "/", not "". A leading pair of + // separators is never stripped, to support alternate roots. This is used to + // support UNC paths on Windows. + void StripTrailingSeparatorsInternal(); + + StringType path_; +}; + +std::ostream& operator<<(std::ostream& out, const FilePath& file_path); + +} // namespace base + +// Macros for string literal initialization of FilePath::CharType[], and for +// using a FilePath::CharType[] in a printf-style format string. +#if defined(OS_WIN) +#define FILE_PATH_LITERAL(x) L##x +#define PRFilePath "ls" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#define FILE_PATH_LITERAL(x) x +#define PRFilePath "s" +#endif // OS_WIN + +namespace std { + +template <> +struct hash { + typedef base::FilePath argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type const& f) const { + return hash()(f.value()); + } +}; + +} // namespace std + +#endif // BASE_FILES_FILE_PATH_H_ diff --git a/src/3rdparty/gn/base/files/file_path_constants.cc b/src/3rdparty/gn/base/files/file_path_constants.cc new file mode 100644 index 00000000000..fc7d6636420 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_path_constants.cc @@ -0,0 +1,25 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/files/file_path.h" +#include "base/macros.h" + +namespace base { + +#if defined(FILE_PATH_USES_WIN_SEPARATORS) +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); +#else // FILE_PATH_USES_WIN_SEPARATORS +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); +#endif // FILE_PATH_USES_WIN_SEPARATORS + +const size_t FilePath::kSeparatorsLength = arraysize(kSeparators); + +const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); +const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); + +const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_posix.cc b/src/3rdparty/gn/base/files/file_posix.cc new file mode 100644 index 00000000000..ed9a5e2a8e3 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_posix.cc @@ -0,0 +1,434 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file.h" + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/utf_string_conversions.h" +#include "util/build_config.h" + +namespace base { + +// Make sure our Whence mappings match the system headers. +static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR && + File::FROM_END == SEEK_END, + "whence mapping must match the system headers"); + +namespace { + +#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ + defined(OS_ANDROID) && __ANDROID_API__ < 21 +int CallFstat(int fd, stat_wrapper_t* sb) { + return fstat(fd, sb); +} +#else +int CallFstat(int fd, stat_wrapper_t* sb) { + return fstat64(fd, sb); +} +#endif + +// NaCl doesn't provide the following system calls, so either simulate them or +// wrap them in order to minimize the number of #ifdef's in this file. +#if !defined(OS_NACL) && !defined(OS_AIX) +bool IsOpenAppend(PlatformFile file) { + return (fcntl(file, F_GETFL) & O_APPEND) != 0; +} + +int CallFtruncate(PlatformFile file, int64_t length) { + return HANDLE_EINTR(ftruncate(file, length)); +} + +#if !defined(OS_FUCHSIA) +File::Error CallFcntlFlock(PlatformFile file, bool do_lock) { + struct flock lock; + lock.l_type = do_lock ? F_WRLCK : F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; // Lock entire file. + if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1) + return File::GetLastFileError(); + return File::FILE_OK; +} +#endif + +#else // defined(OS_NACL) && !defined(OS_AIX) + +bool IsOpenAppend(PlatformFile file) { + // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX + // standard and always appends if the file is opened with O_APPEND, just + // return false here. + return false; +} + +int CallFtruncate(PlatformFile file, int64_t length) { + NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate. + return 0; +} + +File::Error CallFcntlFlock(PlatformFile file, bool do_lock) { + NOTIMPLEMENTED(); // NaCl doesn't implement flock struct. + return File::FILE_ERROR_INVALID_OPERATION; +} +#endif // defined(OS_NACL) + +} // namespace + +void File::Info::FromStat(const stat_wrapper_t& stat_info) { + is_directory = S_ISDIR(stat_info.st_mode); + is_symbolic_link = S_ISLNK(stat_info.st_mode); + size = stat_info.st_size; + +#if defined(OS_MACOSX) + time_t last_modified_sec = stat_info.st_mtimespec.tv_sec; + int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec; + time_t last_accessed_sec = stat_info.st_atimespec.tv_sec; + int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec; + time_t creation_time_sec = stat_info.st_ctimespec.tv_sec; + int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec; +#elif defined(OS_AIX) + time_t last_modified_sec = stat_info.st_mtime; + int64_t last_modified_nsec = 0; + time_t last_accessed_sec = stat_info.st_atime; + int64_t last_accessed_nsec = 0; + time_t creation_time_sec = stat_info.st_ctime; + int64_t creation_time_nsec = 0; +#elif defined(OS_POSIX) + time_t last_modified_sec = stat_info.st_mtim.tv_sec; + int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec; + time_t last_accessed_sec = stat_info.st_atim.tv_sec; + int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec; + time_t creation_time_sec = stat_info.st_ctim.tv_sec; + int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec; +#else +#error +#endif + + constexpr uint64_t kNano = 1'000'000'000; + last_modified = last_modified_sec * kNano + last_modified_nsec; + last_accessed = last_accessed_sec * kNano + last_accessed_nsec; + creation_time = creation_time_sec * kNano + creation_time_nsec; +} + +bool File::IsValid() const { + return file_.is_valid(); +} + +PlatformFile File::GetPlatformFile() const { + return file_.get(); +} + +PlatformFile File::TakePlatformFile() { + return file_.release(); +} + +void File::Close() { + if (!IsValid()) + return; + + file_.reset(); +} + +int64_t File::Seek(Whence whence, int64_t offset) { + DCHECK(IsValid()); + + static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits"); + return lseek(file_.get(), static_cast(offset), + static_cast(whence)); +} + +int File::Read(int64_t offset, char* data, int size) { + DCHECK(IsValid()); + if (size < 0) + return -1; + + int bytes_read = 0; + int rv; + do { + rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read, + offset + bytes_read)); + if (rv <= 0) + break; + + bytes_read += rv; + } while (bytes_read < size); + + return bytes_read ? bytes_read : rv; +} + +int File::ReadAtCurrentPos(char* data, int size) { + DCHECK(IsValid()); + if (size < 0) + return -1; + + int bytes_read = 0; + int rv; + do { + rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read)); + if (rv <= 0) + break; + + bytes_read += rv; + } while (bytes_read < size); + + return bytes_read ? bytes_read : rv; +} + +int File::ReadNoBestEffort(int64_t offset, char* data, int size) { + DCHECK(IsValid()); + return HANDLE_EINTR(pread(file_.get(), data, size, offset)); +} + +int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { + DCHECK(IsValid()); + if (size < 0) + return -1; + + return HANDLE_EINTR(read(file_.get(), data, size)); +} + +int File::Write(int64_t offset, const char* data, int size) { + if (IsOpenAppend(file_.get())) + return WriteAtCurrentPos(data, size); + + DCHECK(IsValid()); + if (size < 0) + return -1; + + int bytes_written = 0; + int rv; + do { + rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written, + size - bytes_written, offset + bytes_written)); + if (rv <= 0) + break; + + bytes_written += rv; + } while (bytes_written < size); + + return bytes_written ? bytes_written : rv; +} + +int File::WriteAtCurrentPos(const char* data, int size) { + DCHECK(IsValid()); + if (size < 0) + return -1; + + int bytes_written = 0; + int rv; + do { + rv = HANDLE_EINTR( + write(file_.get(), data + bytes_written, size - bytes_written)); + if (rv <= 0) + break; + + bytes_written += rv; + } while (bytes_written < size); + + return bytes_written ? bytes_written : rv; +} + +int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { + DCHECK(IsValid()); + if (size < 0) + return -1; + + return HANDLE_EINTR(write(file_.get(), data, size)); +} + +int64_t File::GetLength() { + DCHECK(IsValid()); + + stat_wrapper_t file_info; + if (CallFstat(file_.get(), &file_info)) + return -1; + + return file_info.st_size; +} + +bool File::SetLength(int64_t length) { + DCHECK(IsValid()); + + return !CallFtruncate(file_.get(), length); +} + +bool File::GetInfo(Info* info) { + DCHECK(IsValid()); + + stat_wrapper_t file_info; + if (CallFstat(file_.get(), &file_info)) + return false; + + info->FromStat(file_info); + return true; +} + +#if !defined(OS_FUCHSIA) +File::Error File::Lock() { + return CallFcntlFlock(file_.get(), true); +} + +File::Error File::Unlock() { + return CallFcntlFlock(file_.get(), false); +} +#endif + +File File::Duplicate() const { + if (!IsValid()) + return File(); + + PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile())); + if (other_fd == -1) + return File(File::GetLastFileError()); + + File other(other_fd); + if (async()) + other.async_ = true; + return other; +} + +// Static. +File::Error File::OSErrorToFileError(int saved_errno) { + switch (saved_errno) { + case EACCES: + case EISDIR: + case EROFS: + case EPERM: + return FILE_ERROR_ACCESS_DENIED; + case EBUSY: +#if !defined(OS_NACL) // ETXTBSY not defined by NaCl. + case ETXTBSY: +#endif + return FILE_ERROR_IN_USE; + case EEXIST: + return FILE_ERROR_EXISTS; + case EIO: + return FILE_ERROR_IO; + case ENOENT: + return FILE_ERROR_NOT_FOUND; + case ENFILE: // fallthrough + case EMFILE: + return FILE_ERROR_TOO_MANY_OPENED; + case ENOMEM: + return FILE_ERROR_NO_MEMORY; + case ENOSPC: + return FILE_ERROR_NO_SPACE; + case ENOTDIR: + return FILE_ERROR_NOT_A_DIRECTORY; + default: + // This function should only be called for errors. + DCHECK_NE(0, saved_errno); + return FILE_ERROR_FAILED; + } +} + +// NaCl doesn't implement system calls to open files directly. +#if !defined(OS_NACL) +// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here? +void File::DoInitialize(const FilePath& path, uint32_t flags) { + DCHECK(!IsValid()); + + int open_flags = 0; + if (flags & FLAG_CREATE) + open_flags = O_CREAT | O_EXCL; + + created_ = false; + + if (flags & FLAG_CREATE_ALWAYS) { + DCHECK(!open_flags); + DCHECK(flags & FLAG_WRITE); + open_flags = O_CREAT | O_TRUNC; + } + + if (flags & FLAG_OPEN_TRUNCATED) { + DCHECK(!open_flags); + DCHECK(flags & FLAG_WRITE); + open_flags = O_TRUNC; + } + + if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) { + NOTREACHED(); + errno = EOPNOTSUPP; + error_details_ = FILE_ERROR_FAILED; + return; + } + + if (flags & FLAG_WRITE && flags & FLAG_READ) { + open_flags |= O_RDWR; + } else if (flags & FLAG_WRITE) { + open_flags |= O_WRONLY; + } else if (!(flags & FLAG_READ) && !(flags & FLAG_WRITE_ATTRIBUTES) && + !(flags & FLAG_APPEND) && !(flags & FLAG_OPEN_ALWAYS)) { + NOTREACHED(); + } + + if (flags & FLAG_TERMINAL_DEVICE) + open_flags |= O_NOCTTY | O_NDELAY; + + if (flags & FLAG_APPEND && flags & FLAG_READ) + open_flags |= O_APPEND | O_RDWR; + else if (flags & FLAG_APPEND) + open_flags |= O_APPEND | O_WRONLY; + + static_assert(O_RDONLY == 0, "O_RDONLY must equal zero"); + + int mode = S_IRUSR | S_IWUSR; + int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode)); + + if (flags & FLAG_OPEN_ALWAYS) { + if (descriptor < 0) { + open_flags |= O_CREAT; + if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE) + open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW + + descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode)); + if (descriptor >= 0) + created_ = true; + } + } + + if (descriptor < 0) { + error_details_ = File::GetLastFileError(); + return; + } + + if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) + created_ = true; + + if (flags & FLAG_DELETE_ON_CLOSE) + unlink(path.value().c_str()); + + async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); + error_details_ = FILE_OK; + file_.reset(descriptor); +} +#endif // !defined(OS_NACL) + +bool File::Flush() { + DCHECK(IsValid()); + +#if defined(OS_LINUX) + return !HANDLE_EINTR(fdatasync(file_.get())); +#else + return !HANDLE_EINTR(fsync(file_.get())); +#endif +} + +void File::SetPlatformFile(PlatformFile file) { + DCHECK(!file_.is_valid()); + file_.reset(file); +} + +// static +File::Error File::GetLastFileError() { + return base::File::OSErrorToFileError(errno); +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_util.cc b/src/3rdparty/gn/base/files/file_util.cc new file mode 100644 index 00000000000..9a98a0b81e0 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_util.cc @@ -0,0 +1,263 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_util.h" + +#if defined(OS_WIN) +#include +#endif +#include + +#include +#include + +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "util/build_config.h" + +namespace base { + +#if !defined(OS_NACL_NONSFI) +namespace { + +// The maximum number of 'uniquified' files we will try to create. +// This is used when the filename we're trying to download is already in use, +// so we create a new unique filename by appending " (nnn)" before the +// extension, where 1 <= nnn <= kMaxUniqueFiles. +// Also used by code that cleans up said files. +static const int kMaxUniqueFiles = 100; + +} // namespace + +int64_t ComputeDirectorySize(const FilePath& root_path) { + int64_t running_size = 0; + FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); + while (!file_iter.Next().empty()) + running_size += file_iter.GetInfo().GetSize(); + return running_size; +} + +bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { + // We open the file in binary format even if they are text files because + // we are just comparing that bytes are exactly same in both files and not + // doing anything smart with text formatting. + std::ifstream file1(filename1.value().c_str(), + std::ios::in | std::ios::binary); + std::ifstream file2(filename2.value().c_str(), + std::ios::in | std::ios::binary); + + // Even if both files aren't openable (and thus, in some sense, "equal"), + // any unusable file yields a result of "false". + if (!file1.is_open() || !file2.is_open()) + return false; + + const int BUFFER_SIZE = 2056; + char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; + do { + file1.read(buffer1, BUFFER_SIZE); + file2.read(buffer2, BUFFER_SIZE); + + if ((file1.eof() != file2.eof()) || (file1.gcount() != file2.gcount()) || + (memcmp(buffer1, buffer2, static_cast(file1.gcount())))) { + file1.close(); + file2.close(); + return false; + } + } while (!file1.eof() || !file2.eof()); + + file1.close(); + file2.close(); + return true; +} + +bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { + std::ifstream file1(filename1.value().c_str(), std::ios::in); + std::ifstream file2(filename2.value().c_str(), std::ios::in); + + // Even if both files aren't openable (and thus, in some sense, "equal"), + // any unusable file yields a result of "false". + if (!file1.is_open() || !file2.is_open()) + return false; + + do { + std::string line1, line2; + getline(file1, line1); + getline(file2, line2); + + // Check for mismatched EOF states, or any error state. + if ((file1.eof() != file2.eof()) || file1.bad() || file2.bad()) { + return false; + } + + // Trim all '\r' and '\n' characters from the end of the line. + std::string::size_type end1 = line1.find_last_not_of("\r\n"); + if (end1 == std::string::npos) + line1.clear(); + else if (end1 + 1 < line1.length()) + line1.erase(end1 + 1); + + std::string::size_type end2 = line2.find_last_not_of("\r\n"); + if (end2 == std::string::npos) + line2.clear(); + else if (end2 + 1 < line2.length()) + line2.erase(end2 + 1); + + if (line1 != line2) + return false; + } while (!file1.eof() || !file2.eof()); + + return true; +} +#endif // !defined(OS_NACL_NONSFI) + +bool ReadFileToStringWithMaxSize(const FilePath& path, + std::string* contents, + size_t max_size) { + if (contents) + contents->clear(); + if (path.ReferencesParent()) + return false; + FILE* file = OpenFile(path, "rb"); + if (!file) { + return false; + } + + // Many files supplied in |path| have incorrect size (proc files etc). + // Hence, the file is read sequentially as opposed to a one-shot read, using + // file size as a hint for chunk size if available. + constexpr int64_t kDefaultChunkSize = 1 << 16; + int64_t chunk_size; +#if !defined(OS_NACL_NONSFI) + if (!GetFileSize(path, &chunk_size) || chunk_size <= 0) + chunk_size = kDefaultChunkSize - 1; + // We need to attempt to read at EOF for feof flag to be set so here we + // use |chunk_size| + 1. + chunk_size = std::min(chunk_size, max_size) + 1; +#else + chunk_size = kDefaultChunkSize; +#endif // !defined(OS_NACL_NONSFI) + size_t bytes_read_this_pass; + size_t bytes_read_so_far = 0; + bool read_status = true; + std::string local_contents; + local_contents.resize(chunk_size); + + while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1, + chunk_size, file)) > 0) { + if ((max_size - bytes_read_so_far) < bytes_read_this_pass) { + // Read more than max_size bytes, bail out. + bytes_read_so_far = max_size; + read_status = false; + break; + } + // In case EOF was not reached, iterate again but revert to the default + // chunk size. + if (bytes_read_so_far == 0) + chunk_size = kDefaultChunkSize; + + bytes_read_so_far += bytes_read_this_pass; + // Last fread syscall (after EOF) can be avoided via feof, which is just a + // flag check. + if (feof(file)) + break; + local_contents.resize(bytes_read_so_far + chunk_size); + } + read_status = read_status && !ferror(file); + CloseFile(file); + if (contents) { + contents->swap(local_contents); + contents->resize(bytes_read_so_far); + } + + return read_status; +} + +bool ReadFileToString(const FilePath& path, std::string* contents) { + return ReadFileToStringWithMaxSize(path, contents, + std::numeric_limits::max()); +} + +#if !defined(OS_NACL_NONSFI) +bool IsDirectoryEmpty(const FilePath& dir_path) { + FileEnumerator files(dir_path, false, + FileEnumerator::FILES | FileEnumerator::DIRECTORIES); + if (files.Next().empty()) + return true; + return false; +} + +FILE* CreateAndOpenTemporaryFile(FilePath* path) { + FilePath directory; + if (!GetTempDir(&directory)) + return nullptr; + + return CreateAndOpenTemporaryFileInDir(directory, path); +} + +bool CreateDirectory(const FilePath& full_path) { + return CreateDirectoryAndGetError(full_path, nullptr); +} + +bool GetFileSize(const FilePath& file_path, int64_t* file_size) { + File::Info info; + if (!GetFileInfo(file_path, &info)) + return false; + *file_size = info.size; + return true; +} + +#endif // !defined(OS_NACL_NONSFI) + +bool CloseFile(FILE* file) { + if (file == nullptr) + return true; + return fclose(file) == 0; +} + +#if !defined(OS_NACL_NONSFI) +bool TruncateFile(FILE* file) { + if (file == nullptr) + return false; + long current_offset = ftell(file); + if (current_offset == -1) + return false; +#if defined(OS_WIN) + int fd = _fileno(file); + if (_chsize(fd, current_offset) != 0) + return false; +#else + int fd = fileno(file); + if (ftruncate(fd, current_offset) != 0) + return false; +#endif + return true; +} + +int GetUniquePathNumber(const FilePath& path, + const FilePath::StringType& suffix) { + bool have_suffix = !suffix.empty(); + if (!PathExists(path) && + (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { + return 0; + } + + FilePath new_path; + for (int count = 1; count <= kMaxUniqueFiles; ++count) { + new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count)); + if (!PathExists(new_path) && + (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { + return count; + } + } + + return -1; +} +#endif // !defined(OS_NACL_NONSFI) + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_util.h b/src/3rdparty/gn/base/files/file_util.h new file mode 100644 index 00000000000..e22f40f8f53 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_util.h @@ -0,0 +1,395 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains utility functions for dealing with the local +// filesystem. + +#ifndef BASE_FILES_FILE_UTIL_H_ +#define BASE_FILES_FILE_UTIL_H_ + +#include +#include +#include + +#include +#include +#include + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include +#endif + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/strings/string16.h" +#include "util/build_config.h" + +#if defined(OS_WIN) +#include "base/win/windows_types.h" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#endif + +namespace base { + +class Environment; + +//----------------------------------------------------------------------------- +// Functions that involve filesystem access or modification: + +// Returns an absolute version of a relative path. Returns an empty path on +// error. On POSIX, this function fails if the path does not exist. This +// function can result in I/O so it can be slow. +FilePath MakeAbsoluteFilePath(const FilePath& input); + +// Returns the total number of bytes used by all the files under |root_path|. +// If the path does not exist the function returns 0. +// +// This function is implemented using the FileEnumerator class so it is not +// particularly speedy in any platform. +int64_t ComputeDirectorySize(const FilePath& root_path); + +// Deletes the given path, whether it's a file or a directory. +// If it's a directory, it's perfectly happy to delete all of the +// directory's contents. Passing true to recursive deletes +// subdirectories and their contents as well. +// Returns true if successful, false otherwise. It is considered successful +// to attempt to delete a file that does not exist. +// +// In posix environment and if |path| is a symbolic link, this deletes only +// the symlink. (even if the symlink points to a non-existent file) +// +// WARNING: USING THIS WITH recursive==true IS EQUIVALENT +// TO "rm -rf", SO USE WITH CAUTION. +bool DeleteFile(const FilePath& path, bool recursive); + +#if defined(OS_WIN) +// Schedules to delete the given path, whether it's a file or a directory, until +// the operating system is restarted. +// Note: +// 1) The file/directory to be deleted should exist in a temp folder. +// 2) The directory to be deleted must be empty. +bool DeleteFileAfterReboot(const FilePath& path); +#endif + +// Renames file |from_path| to |to_path|. Both paths must be on the same +// volume, or the function will fail. Destination file will be created +// if it doesn't exist. Prefer this function over Move when dealing with +// temporary files. On Windows it preserves attributes of the target file. +// Returns true on success, leaving *error unchanged. +// Returns false on failure and sets *error appropriately, if it is non-NULL. +bool ReplaceFile(const FilePath& from_path, + const FilePath& to_path, + File::Error* error); + +// Returns true if the given path exists on the local filesystem, +// false otherwise. +bool PathExists(const FilePath& path); + +// Returns true if the given path is writable by the user, false otherwise. +bool PathIsWritable(const FilePath& path); + +// Returns true if the given path exists and is a directory, false otherwise. +bool DirectoryExists(const FilePath& path); + +// Returns true if the contents of the two files given are equal, false +// otherwise. If either file can't be read, returns false. +bool ContentsEqual(const FilePath& filename1, const FilePath& filename2); + +// Returns true if the contents of the two text files given are equal, false +// otherwise. This routine treats "\r\n" and "\n" as equivalent. +bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2); + +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). +bool ReadFileToString(const FilePath& path, std::string* contents); + +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. When the file size exceeds |max_size|, the +// function returns false with |contents| holding the file truncated to +// |max_size|. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). +bool ReadFileToStringWithMaxSize(const FilePath& path, + std::string* contents, + size_t max_size); + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + +// Read exactly |bytes| bytes from file descriptor |fd|, storing the result +// in |buffer|. This function is protected against EINTR and partial reads. +// Returns true iff |bytes| bytes have been successfully read from |fd|. +bool ReadFromFD(int fd, char* buffer, size_t bytes); + +// Performs the same function as CreateAndOpenTemporaryFileInDir(), but returns +// the file-descriptor directly, rather than wrapping it into a FILE. Returns +// -1 on failure. +int CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir, FilePath* path); + +#endif // OS_POSIX || OS_FUCHSIA + +#if defined(OS_POSIX) + +// Creates a symbolic link at |symlink| pointing to |target|. Returns +// false on failure. +bool CreateSymbolicLink(const FilePath& target, const FilePath& symlink); + +// Reads the given |symlink| and returns where it points to in |target|. +// Returns false upon failure. +bool ReadSymbolicLink(const FilePath& symlink, FilePath* target); + +// Bits and masks of the file permission. +enum FilePermissionBits { + FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO, + FILE_PERMISSION_USER_MASK = S_IRWXU, + FILE_PERMISSION_GROUP_MASK = S_IRWXG, + FILE_PERMISSION_OTHERS_MASK = S_IRWXO, + + FILE_PERMISSION_READ_BY_USER = S_IRUSR, + FILE_PERMISSION_WRITE_BY_USER = S_IWUSR, + FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR, + FILE_PERMISSION_READ_BY_GROUP = S_IRGRP, + FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP, + FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP, + FILE_PERMISSION_READ_BY_OTHERS = S_IROTH, + FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH, + FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH, +}; + +// Reads the permission of the given |path|, storing the file permission +// bits in |mode|. If |path| is symbolic link, |mode| is the permission of +// a file which the symlink points to. +bool GetPosixFilePermissions(const FilePath& path, int* mode); +// Sets the permission of the given |path|. If |path| is symbolic link, sets +// the permission of a file which the symlink points to. +bool SetPosixFilePermissions(const FilePath& path, int mode); + +// Returns true iff |executable| can be found in any directory specified by the +// environment variable in |env|. +bool ExecutableExistsInPath(Environment* env, + const FilePath::StringType& executable); + +#endif // OS_POSIX + +// Returns true if the given directory is empty +bool IsDirectoryEmpty(const FilePath& dir_path); + +// Get the temporary directory provided by the system. +// +// WARNING: In general, you should use CreateTemporaryFile variants below +// instead of this function. Those variants will ensure that the proper +// permissions are set so that other users on the system can't edit them while +// they're open (which can lead to security issues). +bool GetTempDir(FilePath* path); + +// Creates a temporary file. The full path is placed in |path|, and the +// function returns true if was successful in creating the file. The file will +// be empty and all handles closed after this function returns. +bool CreateTemporaryFile(FilePath* path); + +// Same as CreateTemporaryFile but the file is created in |dir|. +bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file); + +// Create and open a temporary file. File is opened for read/write. +// The full path is placed in |path|. +// Returns a handle to the opened file or NULL if an error occurred. +FILE* CreateAndOpenTemporaryFile(FilePath* path); + +// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path); + +// Create a new directory. If prefix is provided, the new directory name is in +// the format of prefixyyyy. +// NOTE: prefix is ignored in the POSIX implementation. +// If success, return true and output the full path of the directory created. +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path); + +// Create a directory within another directory. +// Extra characters will be appended to |prefix| to ensure that the +// new directory does not have the same name as an existing directory. +bool CreateTemporaryDirInDir(const FilePath& base_dir, + const FilePath::StringType& prefix, + FilePath* new_dir); + +// Creates a directory, as well as creating any parent directories, if they +// don't exist. Returns 'true' on successful creation, or if the directory +// already exists. The directory is only readable by the current user. +// Returns true on success, leaving *error unchanged. +// Returns false on failure and sets *error appropriately, if it is non-NULL. +bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error); + +// Backward-compatible convenience method for the above. +bool CreateDirectory(const FilePath& full_path); + +// Returns the file size. Returns true on success. +bool GetFileSize(const FilePath& file_path, int64_t* file_size); + +// Sets |real_path| to |path| with symbolic links and junctions expanded. +// On windows, make sure the path starts with a lettered drive. +// |path| must reference a file. Function will fail if |path| points to +// a directory or to a nonexistent path. On windows, this function will +// fail if |path| is a junction or symlink that points to an empty file, +// or if |real_path| would be longer than MAX_PATH characters. +bool NormalizeFilePath(const FilePath& path, FilePath* real_path); + +#if defined(OS_WIN) + +// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."), +// return in |drive_letter_path| the equivalent path that starts with +// a drive letter ("C:\..."). Return false if no such path exists. +bool DevicePathToDriveLetterPath(const FilePath& device_path, + FilePath* drive_letter_path); + +// Given an existing file in |path|, set |real_path| to the path +// in native NT format, of the form "\Device\HarddiskVolumeXX\..". +// Returns false if the path can not be found. Empty files cannot +// be resolved with this function. +bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path); +#endif + +// This function will return if the given file is a symlink or not. +bool IsLink(const FilePath& file_path); + +// Returns information about the given file path. +bool GetFileInfo(const FilePath& file_path, File::Info* info); + +// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The +// underlying file descriptor (POSIX) or handle (Windows) is unconditionally +// configured to not be propagated to child processes. +FILE* OpenFile(const FilePath& filename, const char* mode); + +// Closes file opened by OpenFile. Returns true on success. +bool CloseFile(FILE* file); + +// Associates a standard FILE stream with an existing File. Note that this +// functions take ownership of the existing File. +FILE* FileToFILE(File file, const char* mode); + +// Truncates an open file to end at the location of the current file pointer. +// This is a cross-platform analog to Windows' SetEndOfFile() function. +bool TruncateFile(FILE* file); + +// Reads at most the given number of bytes from the file into the buffer. +// Returns the number of read bytes, or -1 on error. +int ReadFile(const FilePath& filename, char* data, int max_size); + +// Writes the given buffer into the file, overwriting any data that was +// previously there. Returns the number of bytes written, or -1 on error. +int WriteFile(const FilePath& filename, const char* data, int size); + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// Appends |data| to |fd|. Does not close |fd| when done. Returns true iff +// |size| bytes of |data| were written to |fd|. +bool WriteFileDescriptor(const int fd, const char* data, int size); +#endif + +// Appends |data| to |filename|. Returns true iff |size| bytes of |data| were +// written to |filename|. +bool AppendToFile(const FilePath& filename, const char* data, int size); + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* path); + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& path); + +// Attempts to find a number that can be appended to the |path| to make it +// unique. If |path| does not exist, 0 is returned. If it fails to find such +// a number, -1 is returned. If |suffix| is not empty, also checks the +// existence of it with the given suffix. +int GetUniquePathNumber(const FilePath& path, + const FilePath::StringType& suffix); + +// Sets the given |fd| to non-blocking mode. +// Returns true if it was able to set it in the non-blocking mode, otherwise +// false. +bool SetNonBlocking(int fd); + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// Creates a non-blocking, close-on-exec pipe. +// This creates a non-blocking pipe that is not intended to be shared with any +// child process. This will be done atomically if the operating system supports +// it. Returns true if it was able to create the pipe, otherwise false. +bool CreateLocalNonBlockingPipe(int fds[2]); + +// Sets the given |fd| to close-on-exec mode. +// Returns true if it was able to set it in the close-on-exec mode, otherwise +// false. +bool SetCloseOnExec(int fd); + +// Test that |path| can only be changed by a given user and members of +// a given set of groups. +// Specifically, test that all parts of |path| under (and including) |base|: +// * Exist. +// * Are owned by a specific user. +// * Are not writable by all users. +// * Are owned by a member of a given set of groups, or are not writable by +// their group. +// * Are not symbolic links. +// This is useful for checking that a config file is administrator-controlled. +// |base| must contain |path|. +bool VerifyPathControlledByUser(const base::FilePath& base, + const base::FilePath& path, + uid_t owner_uid, + const std::set& group_gids); +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) + +#if defined(OS_MACOSX) && !defined(OS_IOS) +// Is |path| writable only by a user with administrator privileges? +// This function uses Mac OS conventions. The super user is assumed to have +// uid 0, and the administrator group is assumed to be named "admin". +// Testing that |path|, and every parent directory including the root of +// the filesystem, are owned by the superuser, controlled by the group +// "admin", are not writable by all users, and contain no symbolic links. +// Will return false if |path| does not exist. +bool VerifyPathControlledByAdmin(const base::FilePath& path); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + +// Returns the maximum length of path component on the volume containing +// the directory |path|, in the number of FilePath::CharType, or -1 on failure. +int GetMaximumPathComponentLength(const base::FilePath& path); + +#if defined(OS_LINUX) || defined(OS_AIX) +// Broad categories of file systems as returned by statfs() on Linux. +enum FileSystemType { + FILE_SYSTEM_UNKNOWN, // statfs failed. + FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS. + FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2 + FILE_SYSTEM_NFS, + FILE_SYSTEM_SMB, + FILE_SYSTEM_CODA, + FILE_SYSTEM_MEMORY, // in-memory file system + FILE_SYSTEM_CGROUP, // cgroup control. + FILE_SYSTEM_OTHER, // any other value. + FILE_SYSTEM_TYPE_COUNT +}; + +// Attempts determine the FileSystemType for |path|. +// Returns false if |path| doesn't exist. +bool GetFileSystemType(const FilePath& path, FileSystemType* type); +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// Get a temporary directory for shared memory files. The directory may depend +// on whether the destination is intended for executable files, which in turn +// depends on how /dev/shmem was mounted. As a result, you must supply whether +// you intend to create executable shmem segments so this function can find +// an appropriate location. +bool GetShmemTempDir(bool executable, FilePath* path); +#endif + +} // namespace base + +#endif // BASE_FILES_FILE_UTIL_H_ diff --git a/src/3rdparty/gn/base/files/file_util_linux.cc b/src/3rdparty/gn/base/files/file_util_linux.cc new file mode 100644 index 00000000000..b230fd96484 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_util_linux.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_util.h" + +#include +#include +#include + +#include "base/files/file_path.h" + +namespace base { + +bool GetFileSystemType(const FilePath& path, FileSystemType* type) { + struct statfs statfs_buf; + if (statfs(path.value().c_str(), &statfs_buf) < 0) { + if (errno == ENOENT) + return false; + *type = FILE_SYSTEM_UNKNOWN; + return true; + } + + // Not all possible |statfs_buf.f_type| values are in linux/magic.h. + // Missing values are copied from the statfs man page. + switch (statfs_buf.f_type) { + case 0: + *type = FILE_SYSTEM_0; + break; + case EXT2_SUPER_MAGIC: // Also ext3 and ext4 + case MSDOS_SUPER_MAGIC: + case REISERFS_SUPER_MAGIC: + case BTRFS_SUPER_MAGIC: + case 0x5346544E: // NTFS + case 0x58465342: // XFS + case 0x3153464A: // JFS + *type = FILE_SYSTEM_ORDINARY; + break; + case NFS_SUPER_MAGIC: + *type = FILE_SYSTEM_NFS; + break; + case SMB_SUPER_MAGIC: + case 0xFF534D42: // CIFS + *type = FILE_SYSTEM_SMB; + break; + case CODA_SUPER_MAGIC: + *type = FILE_SYSTEM_CODA; + break; + case HUGETLBFS_MAGIC: + case RAMFS_MAGIC: + case TMPFS_MAGIC: + *type = FILE_SYSTEM_MEMORY; + break; + case CGROUP_SUPER_MAGIC: + *type = FILE_SYSTEM_CGROUP; + break; + default: + *type = FILE_SYSTEM_OTHER; + } + return true; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_util_posix.cc b/src/3rdparty/gn/base/files/file_util_posix.cc new file mode 100644 index 00000000000..eb07e64f8e2 --- /dev/null +++ b/src/3rdparty/gn/base/files/file_util_posix.cc @@ -0,0 +1,787 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/command_line.h" +#include "base/containers/stack.h" +#include "base/environment.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/posix/eintr_wrapper.h" +#include "base/stl_util.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "util/build_config.h" + +#if defined(OS_MACOSX) +#include +#endif + +#if !defined(OS_IOS) +#include +#endif + +// We need to do this on AIX due to some inconsistencies in how AIX +// handles XOPEN_SOURCE and ALL_SOURCE. +#if defined(OS_AIX) +extern "C" char* mkdtemp(char* path); +#endif + +namespace base { + +namespace { + +#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ + defined(OS_ANDROID) && __ANDROID_API__ < 21 +int CallStat(const char* path, stat_wrapper_t* sb) { + return stat(path, sb); +} +int CallLstat(const char* path, stat_wrapper_t* sb) { + return lstat(path, sb); +} +#else +int CallStat(const char* path, stat_wrapper_t* sb) { + return stat64(path, sb); +} +int CallLstat(const char* path, stat_wrapper_t* sb) { + return lstat64(path, sb); +} +#endif + +#if !defined(OS_NACL_NONSFI) +// Helper for VerifyPathControlledByUser. +bool VerifySpecificPathControlledByUser(const FilePath& path, + uid_t owner_uid, + const std::set& group_gids) { + stat_wrapper_t stat_info; + if (CallLstat(path.value().c_str(), &stat_info) != 0) { + DPLOG(ERROR) << "Failed to get information on path " << path.value(); + return false; + } + + if (S_ISLNK(stat_info.st_mode)) { + DLOG(ERROR) << "Path " << path.value() << " is a symbolic link."; + return false; + } + + if (stat_info.st_uid != owner_uid) { + DLOG(ERROR) << "Path " << path.value() << " is owned by the wrong user."; + return false; + } + + if ((stat_info.st_mode & S_IWGRP) && + !ContainsKey(group_gids, stat_info.st_gid)) { + DLOG(ERROR) << "Path " << path.value() + << " is writable by an unprivileged group."; + return false; + } + + if (stat_info.st_mode & S_IWOTH) { + DLOG(ERROR) << "Path " << path.value() << " is writable by any user."; + return false; + } + + return true; +} + +std::string TempFileName() { + return std::string(".org.chromium.Chromium.XXXXXX"); +} + +#if defined(OS_LINUX) || defined(OS_AIX) +// Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC. +// This depends on the mount options used for /dev/shm, which vary among +// different Linux distributions and possibly local configuration. It also +// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm +// but its kernel allows mprotect with PROT_EXEC anyway. +bool DetermineDevShmExecutable() { + bool result = false; + FilePath path; + + ScopedFD fd( + CreateAndOpenFdForTemporaryFileInDir(FilePath("/dev/shm"), &path)); + if (fd.is_valid()) { + DeleteFile(path, false); + long sysconf_result = sysconf(_SC_PAGESIZE); + CHECK_GE(sysconf_result, 0); + size_t pagesize = static_cast(sysconf_result); + CHECK_GE(sizeof(pagesize), sizeof(sysconf_result)); + void* mapping = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0); + if (mapping != MAP_FAILED) { + if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0) + result = true; + munmap(mapping, pagesize); + } + } + return result; +} +#endif // defined(OS_LINUX) || defined(OS_AIX) + +bool AdvanceEnumeratorWithStat(FileEnumerator* traversal, + FilePath* out_next_path, + struct stat* out_next_stat) { + DCHECK(out_next_path); + DCHECK(out_next_stat); + *out_next_path = traversal->Next(); + if (out_next_path->empty()) + return false; + + *out_next_stat = traversal->GetInfo().stat(); + return true; +} + +bool CopyFileContents(File* infile, File* outfile) { + static constexpr size_t kBufferSize = 32768; + std::vector buffer(kBufferSize); + + for (;;) { + ssize_t bytes_read = infile->ReadAtCurrentPos(buffer.data(), buffer.size()); + if (bytes_read < 0) + return false; + if (bytes_read == 0) + return true; + // Allow for partial writes + ssize_t bytes_written_per_read = 0; + do { + ssize_t bytes_written_partial = outfile->WriteAtCurrentPos( + &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read); + if (bytes_written_partial < 0) + return false; + + bytes_written_per_read += bytes_written_partial; + } while (bytes_written_per_read < bytes_read); + } + + NOTREACHED(); + return false; +} +#endif // !defined(OS_NACL_NONSFI) + +#if !defined(OS_MACOSX) +// Appends |mode_char| to |mode| before the optional character set encoding; see +// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for +// details. +std::string AppendModeCharacter(StringPiece mode, char mode_char) { + std::string result(mode.as_string()); + size_t comma_pos = result.find(','); + result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1, + mode_char); + return result; +} +#endif + +} // namespace + +#if !defined(OS_NACL_NONSFI) +FilePath MakeAbsoluteFilePath(const FilePath& input) { + char full_path[PATH_MAX]; + if (realpath(input.value().c_str(), full_path) == nullptr) + return FilePath(); + return FilePath(full_path); +} + +// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" +// which works both with and without the recursive flag. I'm not sure we need +// that functionality. If not, remove from file_util_win.cc, otherwise add it +// here. +bool DeleteFile(const FilePath& path, bool recursive) { + const char* path_str = path.value().c_str(); + stat_wrapper_t file_info; + if (CallLstat(path_str, &file_info) != 0) { + // The Windows version defines this condition as success. + return (errno == ENOENT || errno == ENOTDIR); + } + if (!S_ISDIR(file_info.st_mode)) + return (unlink(path_str) == 0); + if (!recursive) + return (rmdir(path_str) == 0); + + bool success = true; + stack directories; + directories.push(path.value()); + FileEnumerator traversal(path, true, + FileEnumerator::FILES | FileEnumerator::DIRECTORIES | + FileEnumerator::SHOW_SYM_LINKS); + for (FilePath current = traversal.Next(); !current.empty(); + current = traversal.Next()) { + if (traversal.GetInfo().IsDirectory()) + directories.push(current.value()); + else + success &= (unlink(current.value().c_str()) == 0); + } + + while (!directories.empty()) { + FilePath dir = FilePath(directories.top()); + directories.pop(); + success &= (rmdir(dir.value().c_str()) == 0); + } + return success; +} + +bool ReplaceFile(const FilePath& from_path, + const FilePath& to_path, + File::Error* error) { + if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) + return true; + if (error) + *error = File::GetLastFileError(); + return false; +} +#endif // !defined(OS_NACL_NONSFI) + +bool CreateLocalNonBlockingPipe(int fds[2]) { +#if defined(OS_LINUX) + return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0; +#else + int raw_fds[2]; + if (pipe(raw_fds) != 0) + return false; + ScopedFD fd_out(raw_fds[0]); + ScopedFD fd_in(raw_fds[1]); + if (!SetCloseOnExec(fd_out.get())) + return false; + if (!SetCloseOnExec(fd_in.get())) + return false; + if (!SetNonBlocking(fd_out.get())) + return false; + if (!SetNonBlocking(fd_in.get())) + return false; + fds[0] = fd_out.release(); + fds[1] = fd_in.release(); + return true; +#endif +} + +bool SetNonBlocking(int fd) { + const int flags = fcntl(fd, F_GETFL); + if (flags == -1) + return false; + if (flags & O_NONBLOCK) + return true; + if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) + return false; + return true; +} + +bool SetCloseOnExec(int fd) { +#if defined(OS_NACL_NONSFI) + const int flags = 0; +#else + const int flags = fcntl(fd, F_GETFD); + if (flags == -1) + return false; + if (flags & FD_CLOEXEC) + return true; +#endif // defined(OS_NACL_NONSFI) + if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1) + return false; + return true; +} + +bool PathExists(const FilePath& path) { + return access(path.value().c_str(), F_OK) == 0; +} + +#if !defined(OS_NACL_NONSFI) +bool PathIsWritable(const FilePath& path) { + return access(path.value().c_str(), W_OK) == 0; +} +#endif // !defined(OS_NACL_NONSFI) + +bool DirectoryExists(const FilePath& path) { + stat_wrapper_t file_info; + if (CallStat(path.value().c_str(), &file_info) != 0) + return false; + return S_ISDIR(file_info.st_mode); +} + +bool ReadFromFD(int fd, char* buffer, size_t bytes) { + size_t total_read = 0; + while (total_read < bytes) { + ssize_t bytes_read = + HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); + if (bytes_read <= 0) + break; + total_read += bytes_read; + } + return total_read == bytes; +} + +#if !defined(OS_NACL_NONSFI) + +int CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory, + FilePath* path) { + *path = directory.Append(TempFileName()); + const std::string& tmpdir_string = path->value(); + // this should be OK since mkstemp just replaces characters in place + char* buffer = const_cast(tmpdir_string.c_str()); + + return HANDLE_EINTR(mkstemp(buffer)); +} + +#if !defined(OS_FUCHSIA) +bool CreateSymbolicLink(const FilePath& target_path, + const FilePath& symlink_path) { + DCHECK(!symlink_path.empty()); + DCHECK(!target_path.empty()); + return ::symlink(target_path.value().c_str(), symlink_path.value().c_str()) != + -1; +} + +bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) { + DCHECK(!symlink_path.empty()); + DCHECK(target_path); + char buf[PATH_MAX]; + ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf)); + + if (count <= 0) { + target_path->clear(); + return false; + } + + *target_path = FilePath(FilePath::StringType(buf, count)); + return true; +} + +bool GetPosixFilePermissions(const FilePath& path, int* mode) { + DCHECK(mode); + + stat_wrapper_t file_info; + // Uses stat(), because on symbolic link, lstat() does not return valid + // permission bits in st_mode + if (CallStat(path.value().c_str(), &file_info) != 0) + return false; + + *mode = file_info.st_mode & FILE_PERMISSION_MASK; + return true; +} + +bool SetPosixFilePermissions(const FilePath& path, int mode) { + DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0); + + // Calls stat() so that we can preserve the higher bits like S_ISGID. + stat_wrapper_t stat_buf; + if (CallStat(path.value().c_str(), &stat_buf) != 0) + return false; + + // Clears the existing permission bits, and adds the new ones. + mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK; + updated_mode_bits |= mode & FILE_PERMISSION_MASK; + + if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0) + return false; + + return true; +} + +bool ExecutableExistsInPath(Environment* env, + const FilePath::StringType& executable) { + std::string path; + if (!env->GetVar("PATH", &path)) { + LOG(ERROR) << "No $PATH variable. Assuming no " << executable << "."; + return false; + } + + for (const StringPiece& cur_path : + SplitStringPiece(path, ":", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { + FilePath file(cur_path); + int permissions; + if (GetPosixFilePermissions(file.Append(executable), &permissions) && + (permissions & FILE_PERMISSION_EXECUTE_BY_USER)) + return true; + } + return false; +} + +#endif // !OS_FUCHSIA + +bool GetTempDir(FilePath* path) { + const char* tmp = getenv("TMPDIR"); + if (tmp) { + *path = FilePath(tmp); + return true; + } + + *path = FilePath("/tmp"); + return true; +} + +#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm. +FilePath GetHomeDir() { + const char* home_dir = getenv("HOME"); + if (home_dir && home_dir[0]) + return FilePath(home_dir); + + FilePath rv; + if (GetTempDir(&rv)) + return rv; + + // Last resort. + return FilePath("/tmp"); +} +#endif // !defined(OS_MACOSX) + +bool CreateTemporaryFile(FilePath* path) { + FilePath directory; + if (!GetTempDir(&directory)) + return false; + int fd = CreateAndOpenFdForTemporaryFileInDir(directory, path); + if (fd < 0) + return false; + close(fd); + return true; +} + +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + int fd = CreateAndOpenFdForTemporaryFileInDir(dir, path); + if (fd < 0) + return nullptr; + + FILE* file = fdopen(fd, "a+"); + if (!file) + close(fd); + return file; +} + +bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { + int fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file); + return ((fd >= 0) && !IGNORE_EINTR(close(fd))); +} + +static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir, + const FilePath::StringType& name_tmpl, + FilePath* new_dir) { + DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos) + << "Directory name template must contain \"XXXXXX\"."; + + FilePath sub_dir = base_dir.Append(name_tmpl); + std::string sub_dir_string = sub_dir.value(); + + // this should be OK since mkdtemp just replaces characters in place + char* buffer = const_cast(sub_dir_string.c_str()); + char* dtemp = mkdtemp(buffer); + if (!dtemp) { + DPLOG(ERROR) << "mkdtemp"; + return false; + } + *new_dir = FilePath(dtemp); + return true; +} + +bool CreateTemporaryDirInDir(const FilePath& base_dir, + const FilePath::StringType& prefix, + FilePath* new_dir) { + FilePath::StringType mkdtemp_template = prefix; + mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX")); + return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir); +} + +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path) { + FilePath tmpdir; + if (!GetTempDir(&tmpdir)) + return false; + + return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path); +} + +bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) { + std::vector subpaths; + + // Collect a list of all parent directories. + FilePath last_path = full_path; + subpaths.push_back(full_path); + for (FilePath path = full_path.DirName(); path.value() != last_path.value(); + path = path.DirName()) { + subpaths.push_back(path); + last_path = path; + } + + // Iterate through the parents and create the missing ones. + for (std::vector::reverse_iterator i = subpaths.rbegin(); + i != subpaths.rend(); ++i) { + if (DirectoryExists(*i)) + continue; + if (mkdir(i->value().c_str(), 0700) == 0) + continue; + // Mkdir failed, but it might have failed with EEXIST, or some other error + // due to the the directory appearing out of thin air. This can occur if + // two processes are trying to create the same file system tree at the same + // time. Check to see if it exists and make sure it is a directory. + int saved_errno = errno; + if (!DirectoryExists(*i)) { + if (error) + *error = File::OSErrorToFileError(saved_errno); + return false; + } + } + return true; +} + +bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { + FilePath real_path_result = MakeAbsoluteFilePath(path); + if (real_path_result.empty()) + return false; + + // To be consistant with windows, fail if |real_path_result| is a + // directory. + if (DirectoryExists(real_path_result)) + return false; + + *normalized_path = real_path_result; + return true; +} + +// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks +// correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948 +bool IsLink(const FilePath& file_path) { + stat_wrapper_t st; + // If we can't lstat the file, it's safe to assume that the file won't at + // least be a 'followable' link. + if (CallLstat(file_path.value().c_str(), &st) != 0) + return false; + return S_ISLNK(st.st_mode); +} + +bool GetFileInfo(const FilePath& file_path, File::Info* results) { + stat_wrapper_t file_info; + if (CallStat(file_path.value().c_str(), &file_info) != 0) + return false; + + results->FromStat(file_info); + return true; +} +#endif // !defined(OS_NACL_NONSFI) + +FILE* OpenFile(const FilePath& filename, const char* mode) { + // 'e' is unconditionally added below, so be sure there is not one already + // present before a comma in |mode|. + DCHECK( + strchr(mode, 'e') == nullptr || + (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ','))); + FILE* result = nullptr; +#if defined(OS_MACOSX) + // macOS does not provide a mode character to set O_CLOEXEC; see + // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html. + const char* the_mode = mode; +#else + std::string mode_with_e(AppendModeCharacter(mode, 'e')); + const char* the_mode = mode_with_e.c_str(); +#endif + do { + result = fopen(filename.value().c_str(), the_mode); + } while (!result && errno == EINTR); +#if defined(OS_MACOSX) + // Mark the descriptor as close-on-exec. + if (result) + SetCloseOnExec(fileno(result)); +#endif + return result; +} + +// NaCl doesn't implement system calls to open files directly. +#if !defined(OS_NACL) +FILE* FileToFILE(File file, const char* mode) { + FILE* stream = fdopen(file.GetPlatformFile(), mode); + if (stream) + file.TakePlatformFile(); + return stream; +} +#endif // !defined(OS_NACL) + +int ReadFile(const FilePath& filename, char* data, int max_size) { + int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY)); + if (fd < 0) + return -1; + + ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size)); + if (IGNORE_EINTR(close(fd)) < 0) + return -1; + return bytes_read; +} + +int WriteFile(const FilePath& filename, const char* data, int size) { + int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666)); + if (fd < 0) + return -1; + + int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1; + if (IGNORE_EINTR(close(fd)) < 0) + return -1; + return bytes_written; +} + +bool WriteFileDescriptor(const int fd, const char* data, int size) { + // Allow for partial writes. + ssize_t bytes_written_total = 0; + for (ssize_t bytes_written_partial = 0; bytes_written_total < size; + bytes_written_total += bytes_written_partial) { + bytes_written_partial = HANDLE_EINTR( + write(fd, data + bytes_written_total, size - bytes_written_total)); + if (bytes_written_partial < 0) + return false; + } + + return true; +} + +#if !defined(OS_NACL_NONSFI) + +bool AppendToFile(const FilePath& filename, const char* data, int size) { + bool ret = true; + int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND)); + if (fd < 0) { + return false; + } + + // This call will either write all of the data or return false. + if (!WriteFileDescriptor(fd, data, size)) { + ret = false; + } + + if (IGNORE_EINTR(close(fd)) < 0) { + return false; + } + + return ret; +} + +bool GetCurrentDirectory(FilePath* dir) { + char system_buffer[PATH_MAX] = ""; + if (!getcwd(system_buffer, sizeof(system_buffer))) { + NOTREACHED(); + return false; + } + *dir = FilePath(system_buffer); + return true; +} + +bool SetCurrentDirectory(const FilePath& path) { + return chdir(path.value().c_str()) == 0; +} + +bool VerifyPathControlledByUser(const FilePath& base, + const FilePath& path, + uid_t owner_uid, + const std::set& group_gids) { + if (base != path && !base.IsParent(path)) { + DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \"" + << base.value() << "\", path = \"" << path.value() << "\""; + return false; + } + + std::vector base_components; + std::vector path_components; + + base.GetComponents(&base_components); + path.GetComponents(&path_components); + + std::vector::const_iterator ib, ip; + for (ib = base_components.begin(), ip = path_components.begin(); + ib != base_components.end(); ++ib, ++ip) { + // |base| must be a subpath of |path|, so all components should match. + // If these CHECKs fail, look at the test that base is a parent of + // path at the top of this function. + DCHECK(ip != path_components.end()); + DCHECK(*ip == *ib); + } + + FilePath current_path = base; + if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids)) + return false; + + for (; ip != path_components.end(); ++ip) { + current_path = current_path.Append(*ip); + if (!VerifySpecificPathControlledByUser(current_path, owner_uid, + group_gids)) + return false; + } + return true; +} + +#if defined(OS_MACOSX) && !defined(OS_IOS) +bool VerifyPathControlledByAdmin(const FilePath& path) { + const unsigned kRootUid = 0; + const FilePath kFileSystemRoot("/"); + + // The name of the administrator group on mac os. + const char* const kAdminGroupNames[] = {"admin", "wheel"}; + + std::set allowed_group_ids; + for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) { + struct group* group_record = getgrnam(kAdminGroupNames[i]); + if (!group_record) { + DPLOG(ERROR) << "Could not get the group ID of group \"" + << kAdminGroupNames[i] << "\"."; + continue; + } + + allowed_group_ids.insert(group_record->gr_gid); + } + + return VerifyPathControlledByUser(kFileSystemRoot, path, kRootUid, + allowed_group_ids); +} +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + +int GetMaximumPathComponentLength(const FilePath& path) { + return pathconf(path.value().c_str(), _PC_NAME_MAX); +} + +bool GetShmemTempDir(bool executable, FilePath* path) { +#if defined(OS_LINUX) || defined(OS_AIX) + bool use_dev_shm = true; + if (executable) { + static const bool s_dev_shm_executable = DetermineDevShmExecutable(); + use_dev_shm = s_dev_shm_executable; + } + if (use_dev_shm) { + *path = FilePath("/dev/shm"); + return true; + } +#endif // defined(OS_LINUX) || defined(OS_AIX) + return GetTempDir(path); +} + +#if !defined(OS_MACOSX) +// Mac has its own implementation, this is for all other Posix systems. +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + File infile; + infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); + if (!infile.IsValid()) + return false; + + File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS); + if (!outfile.IsValid()) + return false; + + return CopyFileContents(&infile, &outfile); +} +#endif // !defined(OS_MACOSX) + +#endif // !defined(OS_NACL_NONSFI) +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_util_win.cc b/src/3rdparty/gn/base/files/file_util_win.cc new file mode 100644 index 00000000000..34d328b5fca --- /dev/null +++ b/src/3rdparty/gn/base/files/file_util_win.cc @@ -0,0 +1,697 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_util.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/scoped_handle.h" + +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +#define SystemFunction036 NTAPI SystemFunction036 +#include +#undef SystemFunction036 + +namespace base { + +namespace { + +const DWORD kFileShareAll = + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + +// Deletes all files and directories in a path. +// Returns ERROR_SUCCESS on success or the Windows error code corresponding to +// the first error encountered. +DWORD DeleteFileRecursive(const FilePath& path, + const FilePath::StringType& pattern, + bool recursive) { + FileEnumerator traversal(path, false, + FileEnumerator::FILES | FileEnumerator::DIRECTORIES, + pattern); + DWORD result = ERROR_SUCCESS; + for (FilePath current = traversal.Next(); !current.empty(); + current = traversal.Next()) { + // Try to clear the read-only bit if we find it. + FileEnumerator::FileInfo info = traversal.GetInfo(); + if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) && + (recursive || !info.IsDirectory())) { + ::SetFileAttributes( + current.value().c_str(), + info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); + } + + DWORD this_result = ERROR_SUCCESS; + if (info.IsDirectory()) { + if (recursive) { + this_result = DeleteFileRecursive(current, pattern, true); + if (this_result == ERROR_SUCCESS && + !::RemoveDirectory(current.value().c_str())) { + this_result = ::GetLastError(); + } + } + } else if (!::DeleteFile(current.value().c_str())) { + this_result = ::GetLastError(); + } + if (result == ERROR_SUCCESS) + result = this_result; + } + return result; +} + +// Appends |mode_char| to |mode| before the optional character set encoding; see +// https://msdn.microsoft.com/library/yeby3zcb.aspx for details. +void AppendModeCharacter(base::char16 mode_char, base::string16* mode) { + size_t comma_pos = mode->find(L','); + mode->insert(comma_pos == base::string16::npos ? mode->length() : comma_pos, + 1, mode_char); +} + +// Returns ERROR_SUCCESS on success, or a Windows error code on failure. +DWORD DoDeleteFile(const FilePath& path, bool recursive) { + if (path.empty()) + return ERROR_SUCCESS; + + if (path.value().length() >= MAX_PATH) + return ERROR_BAD_PATHNAME; + + // Handle any path with wildcards. + if (path.BaseName().value().find_first_of(L"*?") != + FilePath::StringType::npos) { + return DeleteFileRecursive(path.DirName(), path.BaseName().value(), + recursive); + } + + // Report success if the file or path does not exist. + const DWORD attr = ::GetFileAttributes(path.value().c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) { + const DWORD error_code = ::GetLastError(); + return (error_code == ERROR_FILE_NOT_FOUND || + error_code == ERROR_PATH_NOT_FOUND) + ? ERROR_SUCCESS + : error_code; + } + + // Clear the read-only bit if it is set. + if ((attr & FILE_ATTRIBUTE_READONLY) && + !::SetFileAttributes(path.value().c_str(), + attr & ~FILE_ATTRIBUTE_READONLY)) { + return ::GetLastError(); + } + + // Perform a simple delete on anything that isn't a directory. + if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { + return ::DeleteFile(path.value().c_str()) ? ERROR_SUCCESS + : ::GetLastError(); + } + + if (recursive) { + const DWORD error_code = DeleteFileRecursive(path, L"*", true); + if (error_code != ERROR_SUCCESS) + return error_code; + } + return ::RemoveDirectory(path.value().c_str()) ? ERROR_SUCCESS + : ::GetLastError(); +} + +std::string RandomDataToGUIDString(const uint64_t bytes[2]) { + return base::StringPrintf( + "%08x-%04x-%04x-%04x-%012llx", static_cast(bytes[0] >> 32), + static_cast((bytes[0] >> 16) & 0x0000ffff), + static_cast(bytes[0] & 0x0000ffff), + static_cast(bytes[1] >> 48), + bytes[1] & 0x0000ffff'ffffffffULL); +} + +void RandBytes(void* output, size_t output_length) { + char* output_ptr = static_cast(output); + while (output_length > 0) { + const ULONG output_bytes_this_pass = static_cast(std::min( + output_length, static_cast(std::numeric_limits::max()))); + const bool success = + RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; + CHECK(success); + output_length -= output_bytes_this_pass; + output_ptr += output_bytes_this_pass; + } +} + +std::string GenerateGUID() { + uint64_t sixteen_bytes[2]; + // Use base::RandBytes instead of crypto::RandBytes, because crypto calls the + // base version directly, and to prevent the dependency from base/ to crypto/. + RandBytes(&sixteen_bytes, sizeof(sixteen_bytes)); + + // Set the GUID to version 4 as described in RFC 4122, section 4.4. + // The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, + // where y is one of [8, 9, A, B]. + + // Clear the version bits and set the version to 4: + sixteen_bytes[0] &= 0xffffffff'ffff0fffULL; + sixteen_bytes[0] |= 0x00000000'00004000ULL; + + // Set the two most significant bits (bits 6 and 7) of the + // clock_seq_hi_and_reserved to zero and one, respectively: + sixteen_bytes[1] &= 0x3fffffff'ffffffffULL; + sixteen_bytes[1] |= 0x80000000'00000000ULL; + + return RandomDataToGUIDString(sixteen_bytes); +} + +} // namespace + +FilePath MakeAbsoluteFilePath(const FilePath& input) { + wchar_t file_path[MAX_PATH]; + if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH)) + return FilePath(); + return FilePath(file_path); +} + +bool DeleteFile(const FilePath& path, bool recursive) { + static constexpr char kRecursive[] = "DeleteFile.Recursive"; + static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive"; + const StringPiece operation(recursive ? kRecursive : kNonRecursive); + + // Metrics for delete failures tracked in https://crbug.com/599084. Delete may + // fail for a number of reasons. Log some metrics relating to failures in the + // current code so that any improvements or regressions resulting from + // subsequent code changes can be detected. + const DWORD error = DoDeleteFile(path, recursive); + return error == ERROR_SUCCESS; +} + +bool DeleteFileAfterReboot(const FilePath& path) { + if (path.value().length() >= MAX_PATH) + return false; + + return MoveFileEx(path.value().c_str(), NULL, + MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING) != + FALSE; +} + +bool ReplaceFile(const FilePath& from_path, + const FilePath& to_path, + File::Error* error) { + // Try a simple move first. It will only succeed when |to_path| doesn't + // already exist. + if (::MoveFile(from_path.value().c_str(), to_path.value().c_str())) + return true; + File::Error move_error = File::OSErrorToFileError(GetLastError()); + + // Try the full-blown replace if the move fails, as ReplaceFile will only + // succeed when |to_path| does exist. When writing to a network share, we may + // not be able to change the ACLs. Ignore ACL errors then + // (REPLACEFILE_IGNORE_MERGE_ERRORS). + if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL, + REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { + return true; + } + // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that + // |to_path| does not exist. In this case, the more relevant error comes + // from the call to MoveFile. + if (error) { + File::Error replace_error = File::OSErrorToFileError(GetLastError()); + *error = replace_error == File::FILE_ERROR_NOT_FOUND ? move_error + : replace_error; + } + return false; +} + +bool PathExists(const FilePath& path) { + return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); +} + +bool PathIsWritable(const FilePath& path) { + HANDLE dir = + CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (dir == INVALID_HANDLE_VALUE) + return false; + + CloseHandle(dir); + return true; +} + +bool DirectoryExists(const FilePath& path) { + DWORD fileattr = GetFileAttributes(path.value().c_str()); + if (fileattr != INVALID_FILE_ATTRIBUTES) + return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; + return false; +} + +bool GetTempDir(FilePath* path) { + wchar_t temp_path[MAX_PATH + 1]; + DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); + if (path_len >= MAX_PATH || path_len <= 0) + return false; + // TODO(evanm): the old behavior of this function was to always strip the + // trailing slash. We duplicate this here, but it shouldn't be necessary + // when everyone is using the appropriate FilePath APIs. + *path = FilePath(temp_path).StripTrailingSeparators(); + return true; +} + +bool CreateTemporaryFile(FilePath* path) { + FilePath temp_file; + + if (!GetTempDir(path)) + return false; + + if (CreateTemporaryFileInDir(*path, &temp_file)) { + *path = temp_file; + return true; + } + + return false; +} + +// On POSIX we have semantics to create and open a temporary file +// atomically. +// TODO(jrg): is there equivalent call to use on Windows instead of +// going 2-step? +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + if (!CreateTemporaryFileInDir(dir, path)) { + return NULL; + } + // Open file in binary mode, to avoid problems with fwrite. On Windows + // it replaces \n's with \r\n's, which may surprise you. + // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx + return OpenFile(*path, "wb+"); +} + +bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { + // Use GUID instead of ::GetTempFileName() to generate unique file names. + // "Due to the algorithm used to generate file names, GetTempFileName can + // perform poorly when creating a large number of files with the same prefix. + // In such cases, it is recommended that you construct unique file names based + // on GUIDs." + // https://msdn.microsoft.com/library/windows/desktop/aa364991.aspx + + FilePath temp_name; + bool create_file_success = false; + + // Although it is nearly impossible to get a duplicate name with GUID, we + // still use a loop here in case it happens. + for (int i = 0; i < 100; ++i) { + temp_name = dir.Append(ASCIIToUTF16(base::GenerateGUID()) + L".tmp"); + File file(temp_name, + File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); + if (file.IsValid()) { + file.Close(); + create_file_success = true; + break; + } + } + + if (!create_file_success) { + DPLOG(WARNING) << "Failed to get temporary file name in " + << UTF16ToUTF8(dir.value()); + return false; + } + + wchar_t long_temp_name[MAX_PATH + 1]; + DWORD long_name_len = + GetLongPathName(temp_name.value().c_str(), long_temp_name, MAX_PATH); + if (long_name_len > MAX_PATH || long_name_len == 0) { + // GetLongPathName() failed, but we still have a temporary file. + *temp_file = std::move(temp_name); + return true; + } + + FilePath::StringType long_temp_name_str; + long_temp_name_str.assign(long_temp_name, long_name_len); + *temp_file = FilePath(std::move(long_temp_name_str)); + return true; +} + +bool CreateTemporaryDirInDir(const FilePath& base_dir, + const FilePath::StringType& prefix, + FilePath* new_dir) { + FilePath path_to_create; + + for (int count = 0; count < 50; ++count) { + // Try create a new temporary directory with random generated name. If + // the one exists, keep trying another path name until we reach some limit. + string16 new_dir_name; + new_dir_name.assign(prefix); + new_dir_name.append(IntToString16(::GetCurrentProcessId())); + new_dir_name.push_back('_'); + new_dir_name.append(UTF8ToUTF16(GenerateGUID())); + + path_to_create = base_dir.Append(new_dir_name); + if (::CreateDirectory(path_to_create.value().c_str(), NULL)) { + *new_dir = path_to_create; + return true; + } + } + + return false; +} + +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path) { + FilePath system_temp_dir; + if (!GetTempDir(&system_temp_dir)) + return false; + + return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path); +} + +bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) { + // If the path exists, we've succeeded if it's a directory, failed otherwise. + const wchar_t* full_path_str = full_path.value().c_str(); + DWORD fileattr = ::GetFileAttributes(full_path_str); + if (fileattr != INVALID_FILE_ATTRIBUTES) { + if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + return true; + } + DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), " + << "conflicts with existing file."; + if (error) { + *error = File::FILE_ERROR_NOT_A_DIRECTORY; + } + return false; + } + + // Invariant: Path does not exist as file or directory. + + // Attempt to create the parent recursively. This will immediately return + // true if it already exists, otherwise will create all required parent + // directories starting with the highest-level missing parent. + FilePath parent_path(full_path.DirName()); + if (parent_path.value() == full_path.value()) { + if (error) { + *error = File::FILE_ERROR_NOT_FOUND; + } + return false; + } + if (!CreateDirectoryAndGetError(parent_path, error)) { + DLOG(WARNING) << "Failed to create one of the parent directories."; + if (error) { + DCHECK(*error != File::FILE_OK); + } + return false; + } + + if (!::CreateDirectory(full_path_str, NULL)) { + DWORD error_code = ::GetLastError(); + if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) { + // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we + // were racing with someone creating the same directory, or a file + // with the same path. If DirectoryExists() returns true, we lost the + // race to create the same directory. + return true; + } else { + if (error) + *error = File::OSErrorToFileError(error_code); + DLOG(WARNING) << "Failed to create directory " << full_path_str + << ", last error is " << error_code << "."; + return false; + } + } else { + return true; + } +} + +bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { + FilePath mapped_file; + if (!NormalizeToNativeFilePath(path, &mapped_file)) + return false; + // NormalizeToNativeFilePath() will return a path that starts with + // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath() + // will find a drive letter which maps to the path's device, so + // that we return a path starting with a drive letter. + return DevicePathToDriveLetterPath(mapped_file, real_path); +} + +bool DevicePathToDriveLetterPath(const FilePath& nt_device_path, + FilePath* out_drive_letter_path) { + // Get the mapping of drive letters to device paths. + const int kDriveMappingSize = 1024; + wchar_t drive_mapping[kDriveMappingSize] = {'\0'}; + if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) { + DLOG(ERROR) << "Failed to get drive mapping."; + return false; + } + + // The drive mapping is a sequence of null terminated strings. + // The last string is empty. + wchar_t* drive_map_ptr = drive_mapping; + wchar_t device_path_as_string[MAX_PATH]; + wchar_t drive[] = L" :"; + + // For each string in the drive mapping, get the junction that links + // to it. If that junction is a prefix of |device_path|, then we + // know that |drive| is the real path prefix. + while (*drive_map_ptr) { + drive[0] = drive_map_ptr[0]; // Copy the drive letter. + + if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) { + FilePath device_path(device_path_as_string); + if (device_path == nt_device_path || + device_path.IsParent(nt_device_path)) { + *out_drive_letter_path = + FilePath(drive + nt_device_path.value().substr( + wcslen(device_path_as_string))); + return true; + } + } + // Move to the next drive letter string, which starts one + // increment after the '\0' that terminates the current string. + while (*drive_map_ptr++) { + } + } + + // No drive matched. The path does not start with a device junction + // that is mounted as a drive letter. This means there is no drive + // letter path to the volume that holds |device_path|, so fail. + return false; +} + +bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { + // In Vista, GetFinalPathNameByHandle() would give us the real path + // from a file handle. If we ever deprecate XP, consider changing the + // code below to a call to GetFinalPathNameByHandle(). The method this + // function uses is explained in the following msdn article: + // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx + win::ScopedHandle file_handle(::CreateFile(path.value().c_str(), GENERIC_READ, + kFileShareAll, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL)); + if (!file_handle.IsValid()) + return false; + + // Create a file mapping object. Can't easily use MemoryMappedFile, because + // we only map the first byte, and need direct access to the handle. You can + // not map an empty file, this call fails in that case. + win::ScopedHandle file_map_handle( + ::CreateFileMapping(file_handle.Get(), NULL, PAGE_READONLY, 0, + 1, // Just one byte. No need to look at the data. + NULL)); + if (!file_map_handle.IsValid()) + return false; + + // Use a view of the file to get the path to the file. + void* file_view = + MapViewOfFile(file_map_handle.Get(), FILE_MAP_READ, 0, 0, 1); + if (!file_view) + return false; + + // The expansion of |path| into a full path may make it longer. + // GetMappedFileName() will fail if the result is longer than MAX_PATH. + // Pad a bit to be safe. If kMaxPathLength is ever changed to be less + // than MAX_PATH, it would be nessisary to test that GetMappedFileName() + // not return kMaxPathLength. This would mean that only part of the + // path fit in |mapped_file_path|. + const int kMaxPathLength = MAX_PATH + 10; + wchar_t mapped_file_path[kMaxPathLength]; + bool success = false; + HANDLE cp = GetCurrentProcess(); + if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) { + *nt_path = FilePath(mapped_file_path); + success = true; + } + ::UnmapViewOfFile(file_view); + return success; +} + +// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle +// them if we do decide to. +bool IsLink(const FilePath& file_path) { + return false; +} + +bool GetFileInfo(const FilePath& file_path, File::Info* results) { + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesEx(file_path.value().c_str(), GetFileExInfoStandard, + &attr)) { + return false; + } + + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + results->size = size.QuadPart; + + results->is_directory = + (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + results->last_modified = *reinterpret_cast(&attr.ftLastWriteTime); + results->last_accessed = *reinterpret_cast(&attr.ftLastAccessTime); + results->creation_time = *reinterpret_cast(&attr.ftCreationTime); + + return true; +} + +FILE* OpenFile(const FilePath& filename, const char* mode) { + // 'N' is unconditionally added below, so be sure there is not one already + // present before a comma in |mode|. + DCHECK( + strchr(mode, 'N') == nullptr || + (strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ','))); + string16 w_mode = ASCIIToUTF16(mode); + AppendModeCharacter(L'N', &w_mode); + return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO); +} + +FILE* FileToFILE(File file, const char* mode) { + if (!file.IsValid()) + return NULL; + int fd = + _open_osfhandle(reinterpret_cast(file.GetPlatformFile()), 0); + if (fd < 0) + return NULL; + file.TakePlatformFile(); + FILE* stream = _fdopen(fd, mode); + if (!stream) + _close(fd); + return stream; +} + +int ReadFile(const FilePath& filename, char* data, int max_size) { + win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, + NULL)); + if (!file.IsValid()) + return -1; + + DWORD read; + if (::ReadFile(file.Get(), data, max_size, &read, NULL)) + return read; + + return -1; +} + +int WriteFile(const FilePath& filename, const char* data, int size) { + win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL)); + if (!file.IsValid()) { + DPLOG(WARNING) << "CreateFile failed for path " + << UTF16ToUTF8(filename.value()); + return -1; + } + + DWORD written; + BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); + if (result && static_cast(written) == size) + return written; + + if (!result) { + // WriteFile failed. + DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value()) + << " failed"; + } else { + // Didn't write all the bytes. + DLOG(WARNING) << "wrote" << written << " bytes to " + << UTF16ToUTF8(filename.value()) << " expected " << size; + } + return -1; +} + +bool AppendToFile(const FilePath& filename, const char* data, int size) { + win::ScopedHandle file(CreateFile(filename.value().c_str(), FILE_APPEND_DATA, + 0, NULL, OPEN_EXISTING, 0, NULL)); + if (!file.IsValid()) { + return false; + } + + DWORD written; + BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); + if (result && static_cast(written) == size) + return true; + + return false; +} + +bool GetCurrentDirectory(FilePath* dir) { + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); + if (len == 0 || len > MAX_PATH) + return false; + // TODO(evanm): the old behavior of this function was to always strip the + // trailing slash. We duplicate this here, but it shouldn't be necessary + // when everyone is using the appropriate FilePath APIs. + std::wstring dir_str(system_buffer); + *dir = FilePath(dir_str).StripTrailingSeparators(); + return true; +} + +bool SetCurrentDirectory(const FilePath& directory) { + return ::SetCurrentDirectory(directory.value().c_str()) != 0; +} + +int GetMaximumPathComponentLength(const FilePath& path) { + wchar_t volume_path[MAX_PATH]; + if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(), + volume_path, arraysize(volume_path))) { + return -1; + } + + DWORD max_length = 0; + if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL, + NULL, 0)) { + return -1; + } + + // Length of |path| with path separator appended. + size_t prefix = path.StripTrailingSeparators().value().size() + 1; + // The whole path string must be shorter than MAX_PATH. That is, it must be + // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1). + int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast(prefix)); + return std::min(whole_path_limit, static_cast(max_length)); +} + +bool SetNonBlocking(int fd) { + unsigned long nonblocking = 1; + if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0) + return true; + return false; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/file_win.cc b/src/3rdparty/gn/base/files/file_win.cc new file mode 100644 index 00000000000..aff859c94fc --- /dev/null +++ b/src/3rdparty/gn/base/files/file_win.cc @@ -0,0 +1,381 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file.h" + +#include +#include + +#include "base/logging.h" + +#include + +namespace base { + +// Make sure our Whence mappings match the system headers. +static_assert(File::FROM_BEGIN == FILE_BEGIN && + File::FROM_CURRENT == FILE_CURRENT && + File::FROM_END == FILE_END, + "whence mapping must match the system headers"); + +bool File::IsValid() const { + return file_.IsValid(); +} + +PlatformFile File::GetPlatformFile() const { + return file_.Get(); +} + +PlatformFile File::TakePlatformFile() { + return file_.Take(); +} + +void File::Close() { + if (!file_.IsValid()) + return; + + file_.Close(); +} + +int64_t File::Seek(Whence whence, int64_t offset) { + DCHECK(IsValid()); + + LARGE_INTEGER distance, res; + distance.QuadPart = offset; + DWORD move_method = static_cast(whence); + if (!SetFilePointerEx(file_.Get(), distance, &res, move_method)) + return -1; + return res.QuadPart; +} + +int File::Read(int64_t offset, char* data, int size) { + DCHECK(IsValid()); + DCHECK(!async_); + if (size < 0) + return -1; + + LARGE_INTEGER offset_li; + offset_li.QuadPart = offset; + + OVERLAPPED overlapped = {0}; + overlapped.Offset = offset_li.LowPart; + overlapped.OffsetHigh = offset_li.HighPart; + + DWORD bytes_read; + if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped)) + return bytes_read; + if (ERROR_HANDLE_EOF == GetLastError()) + return 0; + + return -1; +} + +int File::ReadAtCurrentPos(char* data, int size) { + DCHECK(IsValid()); + DCHECK(!async_); + if (size < 0) + return -1; + + DWORD bytes_read; + if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL)) + return bytes_read; + if (ERROR_HANDLE_EOF == GetLastError()) + return 0; + + return -1; +} + +int File::ReadNoBestEffort(int64_t offset, char* data, int size) { + // TODO(dbeam): trace this separately? + return Read(offset, data, size); +} + +int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { + // TODO(dbeam): trace this separately? + return ReadAtCurrentPos(data, size); +} + +int File::Write(int64_t offset, const char* data, int size) { + DCHECK(IsValid()); + DCHECK(!async_); + + LARGE_INTEGER offset_li; + offset_li.QuadPart = offset; + + OVERLAPPED overlapped = {0}; + overlapped.Offset = offset_li.LowPart; + overlapped.OffsetHigh = offset_li.HighPart; + + DWORD bytes_written; + if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped)) + return bytes_written; + + return -1; +} + +int File::WriteAtCurrentPos(const char* data, int size) { + DCHECK(IsValid()); + DCHECK(!async_); + if (size < 0) + return -1; + + DWORD bytes_written; + if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL)) + return bytes_written; + + return -1; +} + +int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { + return WriteAtCurrentPos(data, size); +} + +int64_t File::GetLength() { + DCHECK(IsValid()); + + LARGE_INTEGER size; + if (!::GetFileSizeEx(file_.Get(), &size)) + return -1; + + return static_cast(size.QuadPart); +} + +bool File::SetLength(int64_t length) { + DCHECK(IsValid()); + + // Get the current file pointer. + LARGE_INTEGER file_pointer; + LARGE_INTEGER zero; + zero.QuadPart = 0; + if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT)) + return false; + + LARGE_INTEGER length_li; + length_li.QuadPart = length; + // If length > file size, SetFilePointerEx() should extend the file + // with zeroes on all Windows standard file systems (NTFS, FATxx). + if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN)) + return false; + + // Set the new file length and move the file pointer to its old position. + // This is consistent with ftruncate()'s behavior, even when the file + // pointer points to a location beyond the end of the file. + // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not + // promised by the interface (nor was promised by PlatformFile). See if this + // implementation detail can be removed. + return ((::SetEndOfFile(file_.Get()) != FALSE) && + (::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) != + FALSE)); +} + +bool File::GetInfo(Info* info) { + DCHECK(IsValid()); + + BY_HANDLE_FILE_INFORMATION file_info; + if (!GetFileInformationByHandle(file_.Get(), &file_info)) + return false; + + LARGE_INTEGER size; + size.HighPart = file_info.nFileSizeHigh; + size.LowPart = file_info.nFileSizeLow; + info->size = size.QuadPart; + info->is_directory = + (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + info->is_symbolic_link = false; // Windows doesn't have symbolic links. + info->last_modified = + *reinterpret_cast(&file_info.ftLastWriteTime); + info->last_accessed = + *reinterpret_cast(&file_info.ftLastAccessTime); + info->creation_time = *reinterpret_cast(&file_info.ftCreationTime); + return true; +} + +File::Error File::Lock() { + DCHECK(IsValid()); + + BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); + if (!result) + return GetLastFileError(); + return FILE_OK; +} + +File::Error File::Unlock() { + DCHECK(IsValid()); + + BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); + if (!result) + return GetLastFileError(); + return FILE_OK; +} + +File File::Duplicate() const { + if (!IsValid()) + return File(); + + HANDLE other_handle = nullptr; + + if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle + GetPlatformFile(), + GetCurrentProcess(), // hTargetProcessHandle + &other_handle, + 0, // dwDesiredAccess ignored due to SAME_ACCESS + FALSE, // !bInheritHandle + DUPLICATE_SAME_ACCESS)) { + return File(GetLastFileError()); + } + + File other(other_handle); + if (async()) + other.async_ = true; + return other; +} + +bool File::DeleteOnClose(bool delete_on_close) { + FILE_DISPOSITION_INFO disposition = { BOOLEAN(delete_on_close ? TRUE : FALSE) }; + return ::SetFileInformationByHandle(GetPlatformFile(), FileDispositionInfo, + &disposition, sizeof(disposition)) != 0; +} + +// Static. +File::Error File::OSErrorToFileError(DWORD last_error) { + switch (last_error) { + case ERROR_SHARING_VIOLATION: + return FILE_ERROR_IN_USE; + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + return FILE_ERROR_EXISTS; + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return FILE_ERROR_NOT_FOUND; + case ERROR_ACCESS_DENIED: + return FILE_ERROR_ACCESS_DENIED; + case ERROR_TOO_MANY_OPEN_FILES: + return FILE_ERROR_TOO_MANY_OPENED; + case ERROR_OUTOFMEMORY: + case ERROR_NOT_ENOUGH_MEMORY: + return FILE_ERROR_NO_MEMORY; + case ERROR_HANDLE_DISK_FULL: + case ERROR_DISK_FULL: + case ERROR_DISK_RESOURCES_EXHAUSTED: + return FILE_ERROR_NO_SPACE; + case ERROR_USER_MAPPED_FILE: + return FILE_ERROR_INVALID_OPERATION; + case ERROR_NOT_READY: + case ERROR_SECTOR_NOT_FOUND: + case ERROR_DEV_NOT_EXIST: + case ERROR_IO_DEVICE: + case ERROR_FILE_CORRUPT: + case ERROR_DISK_CORRUPT: + return FILE_ERROR_IO; + default: + // This function should only be called for errors. + DCHECK_NE(static_cast(ERROR_SUCCESS), last_error); + return FILE_ERROR_FAILED; + } +} + +void File::DoInitialize(const FilePath& path, uint32_t flags) { + DCHECK(!IsValid()); + + DWORD disposition = 0; + + if (flags & FLAG_OPEN) + disposition = OPEN_EXISTING; + + if (flags & FLAG_CREATE) { + DCHECK(!disposition); + disposition = CREATE_NEW; + } + + if (flags & FLAG_OPEN_ALWAYS) { + DCHECK(!disposition); + disposition = OPEN_ALWAYS; + } + + if (flags & FLAG_CREATE_ALWAYS) { + DCHECK(!disposition); + DCHECK(flags & FLAG_WRITE); + disposition = CREATE_ALWAYS; + } + + if (flags & FLAG_OPEN_TRUNCATED) { + DCHECK(!disposition); + DCHECK(flags & FLAG_WRITE); + disposition = TRUNCATE_EXISTING; + } + + if (!disposition) { + ::SetLastError(ERROR_INVALID_PARAMETER); + error_details_ = FILE_ERROR_FAILED; + NOTREACHED(); + return; + } + + DWORD access = 0; + if (flags & FLAG_WRITE) + access = GENERIC_WRITE; + if (flags & FLAG_APPEND) { + DCHECK(!access); + access = FILE_APPEND_DATA; + } + if (flags & FLAG_READ) + access |= GENERIC_READ; + if (flags & FLAG_WRITE_ATTRIBUTES) + access |= FILE_WRITE_ATTRIBUTES; + if (flags & FLAG_EXECUTE) + access |= GENERIC_EXECUTE; + if (flags & FLAG_CAN_DELETE_ON_CLOSE) + access |= DELETE; + + DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; + if (!(flags & FLAG_EXCLUSIVE_WRITE)) + sharing |= FILE_SHARE_WRITE; + if (flags & FLAG_SHARE_DELETE) + sharing |= FILE_SHARE_DELETE; + + DWORD create_flags = 0; + if (flags & FLAG_ASYNC) + create_flags |= FILE_FLAG_OVERLAPPED; + if (flags & FLAG_TEMPORARY) + create_flags |= FILE_ATTRIBUTE_TEMPORARY; + if (flags & FLAG_HIDDEN) + create_flags |= FILE_ATTRIBUTE_HIDDEN; + if (flags & FLAG_DELETE_ON_CLOSE) + create_flags |= FILE_FLAG_DELETE_ON_CLOSE; + if (flags & FLAG_BACKUP_SEMANTICS) + create_flags |= FILE_FLAG_BACKUP_SEMANTICS; + if (flags & FLAG_SEQUENTIAL_SCAN) + create_flags |= FILE_FLAG_SEQUENTIAL_SCAN; + + file_.Set(CreateFile(path.value().c_str(), access, sharing, NULL, disposition, + create_flags, NULL)); + + if (file_.IsValid()) { + error_details_ = FILE_OK; + async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); + + if (flags & (FLAG_OPEN_ALWAYS)) + created_ = (ERROR_ALREADY_EXISTS != GetLastError()); + else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) + created_ = true; + } else { + error_details_ = GetLastFileError(); + } +} + +bool File::Flush() { + DCHECK(IsValid()); + return ::FlushFileBuffers(file_.Get()) != FALSE; +} + +void File::SetPlatformFile(PlatformFile file) { + file_.Set(file); +} + +// static +File::Error File::GetLastFileError() { + return File::OSErrorToFileError(GetLastError()); +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/platform_file.h b/src/3rdparty/gn/base/files/platform_file.h new file mode 100644 index 00000000000..cba2fc8ad6c --- /dev/null +++ b/src/3rdparty/gn/base/files/platform_file.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_PLATFORM_FILE_H_ +#define BASE_FILES_PLATFORM_FILE_H_ + +#include "base/files/scoped_file.h" +#include "util/build_config.h" + +#if defined(OS_WIN) +#include "base/win/scoped_handle.h" +#include "base/win/windows_types.h" +#endif + +// This file defines platform-independent types for dealing with +// platform-dependent files. If possible, use the higher-level base::File class +// rather than these primitives. + +namespace base { + +#if defined(OS_WIN) + +using PlatformFile = HANDLE; +using ScopedPlatformFile = ::base::win::ScopedHandle; + +// It would be nice to make this constexpr but INVALID_HANDLE_VALUE is a +// ((void*)(-1)) which Clang rejects since reinterpret_cast is technically +// disallowed in constexpr. Visual Studio accepts this, however. +const PlatformFile kInvalidPlatformFile = INVALID_HANDLE_VALUE; + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + +using PlatformFile = int; +using ScopedPlatformFile = ::base::ScopedFD; + +constexpr PlatformFile kInvalidPlatformFile = -1; + +#endif + +} // namespace base + +#endif // BASE_FILES_PLATFORM_FILE_H_ diff --git a/src/3rdparty/gn/base/files/scoped_file.cc b/src/3rdparty/gn/base/files/scoped_file.cc new file mode 100644 index 00000000000..11afedd695e --- /dev/null +++ b/src/3rdparty/gn/base/files/scoped_file.cc @@ -0,0 +1,49 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/scoped_file.h" + +#include "base/logging.h" +#include "util/build_config.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include + +#include "base/posix/eintr_wrapper.h" +#endif + +namespace base { +namespace internal { + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + +// static +void ScopedFDCloseTraits::Free(int fd) { + // It's important to crash here. + // There are security implications to not closing a file descriptor + // properly. As file descriptors are "capabilities", keeping them open + // would make the current process keep access to a resource. Much of + // Chrome relies on being able to "drop" such access. + // It's especially problematic on Linux with the setuid sandbox, where + // a single open directory would bypass the entire security model. + int ret = IGNORE_EINTR(close(fd)); + +#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FUCHSIA) || \ + defined(OS_ANDROID) + // NB: Some file descriptors can return errors from close() e.g. network + // filesystems such as NFS and Linux input devices. On Linux, macOS, and + // Fuchsia's POSIX layer, errors from close other than EBADF do not indicate + // failure to actually close the fd. + if (ret != 0 && errno != EBADF) + ret = 0; +#endif + + PCHECK(0 == ret); +} + +#endif // OS_POSIX || OS_FUCHSIA + +} // namespace internal +} // namespace base diff --git a/src/3rdparty/gn/base/files/scoped_file.h b/src/3rdparty/gn/base/files/scoped_file.h new file mode 100644 index 00000000000..0d896504300 --- /dev/null +++ b/src/3rdparty/gn/base/files/scoped_file.h @@ -0,0 +1,59 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_SCOPED_FILE_H_ +#define BASE_FILES_SCOPED_FILE_H_ + +#include + +#include + +#include "base/logging.h" +#include "base/scoped_generic.h" +#include "util/build_config.h" + +namespace base { + +namespace internal { + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +struct ScopedFDCloseTraits { + static int InvalidValue() { return -1; } + static void Free(int fd); +}; +#endif + +// Functor for |ScopedFILE| (below). +struct ScopedFILECloser { + inline void operator()(FILE* x) const { + if (x) + fclose(x); + } +}; + +} // namespace internal + +// ----------------------------------------------------------------------------- + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// A low-level Posix file descriptor closer class. Use this when writing +// platform-specific code, especially that does non-file-like things with the +// FD (like sockets). +// +// If you're writing low-level Windows code, see base/win/scoped_handle.h +// which provides some additional functionality. +// +// If you're writing cross-platform code that deals with actual files, you +// should generally use base::File instead which can be constructed with a +// handle, and in addition to handling ownership, has convenient cross-platform +// file manipulation functions on it. +typedef ScopedGeneric ScopedFD; +#endif + +// Automatically closes |FILE*|s. +typedef std::unique_ptr ScopedFILE; + +} // namespace base + +#endif // BASE_FILES_SCOPED_FILE_H_ diff --git a/src/3rdparty/gn/base/files/scoped_temp_dir.cc b/src/3rdparty/gn/base/files/scoped_temp_dir.cc new file mode 100644 index 00000000000..01ec0f0caab --- /dev/null +++ b/src/3rdparty/gn/base/files/scoped_temp_dir.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/scoped_temp_dir.h" + +#include "base/files/file_util.h" +#include "base/logging.h" + +namespace base { + +namespace { + +constexpr FilePath::CharType kScopedDirPrefix[] = + FILE_PATH_LITERAL("scoped_dir"); + +} // namespace + +ScopedTempDir::ScopedTempDir() = default; + +ScopedTempDir::~ScopedTempDir() { + if (!path_.empty() && !Delete()) + DLOG(WARNING) << "Could not delete temp dir in dtor."; +} + +bool ScopedTempDir::CreateUniqueTempDir() { + if (!path_.empty()) + return false; + + // This "scoped_dir" prefix is only used on Windows and serves as a template + // for the unique name. + if (!base::CreateNewTempDirectory(kScopedDirPrefix, &path_)) + return false; + + return true; +} + +bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) { + if (!path_.empty()) + return false; + + // If |base_path| does not exist, create it. + if (!base::CreateDirectory(base_path)) + return false; + + // Create a new, uniquely named directory under |base_path|. + if (!base::CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_)) + return false; + + return true; +} + +bool ScopedTempDir::Set(const FilePath& path) { + if (!path_.empty()) + return false; + + if (!DirectoryExists(path) && !base::CreateDirectory(path)) + return false; + + path_ = path; + return true; +} + +bool ScopedTempDir::Delete() { + if (path_.empty()) + return false; + + bool ret = base::DeleteFile(path_, true); + if (ret) { + // We only clear the path if deleted the directory. + path_.clear(); + } + + return ret; +} + +FilePath ScopedTempDir::Take() { + FilePath ret = path_; + path_ = FilePath(); + return ret; +} + +const FilePath& ScopedTempDir::GetPath() const { + DCHECK(!path_.empty()) << "Did you call CreateUniqueTempDir* before?"; + return path_; +} + +bool ScopedTempDir::IsValid() const { + return !path_.empty() && DirectoryExists(path_); +} + +// static +const FilePath::CharType* ScopedTempDir::GetTempDirPrefix() { + return kScopedDirPrefix; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/files/scoped_temp_dir.h b/src/3rdparty/gn/base/files/scoped_temp_dir.h new file mode 100644 index 00000000000..4ddb690945c --- /dev/null +++ b/src/3rdparty/gn/base/files/scoped_temp_dir.h @@ -0,0 +1,70 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_SCOPED_TEMP_DIR_H_ +#define BASE_FILES_SCOPED_TEMP_DIR_H_ + +// An object representing a temporary / scratch directory that should be +// cleaned up (recursively) when this object goes out of scope. Since deletion +// occurs during the destructor, no further error handling is possible if the +// directory fails to be deleted. As a result, deletion is not guaranteed by +// this class. (However note that, whenever possible, by default +// CreateUniqueTempDir creates the directory in a location that is +// automatically cleaned up on reboot, or at other appropriate times.) +// +// Multiple calls to the methods which establish a temporary directory +// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have +// intervening calls to Delete or Take, or the calls will fail. + +#include "base/files/file_path.h" +#include "base/macros.h" + +namespace base { + +class ScopedTempDir { + public: + // No directory is owned/created initially. + ScopedTempDir(); + + // Recursively delete path. + ~ScopedTempDir(); + + // Creates a unique directory in TempPath, and takes ownership of it. + // See file_util::CreateNewTemporaryDirectory. + bool CreateUniqueTempDir() WARN_UNUSED_RESULT; + + // Creates a unique directory under a given path, and takes ownership of it. + bool CreateUniqueTempDirUnderPath(const FilePath& path) WARN_UNUSED_RESULT; + + // Takes ownership of directory at |path|, creating it if necessary. + // Don't call multiple times unless Take() has been called first. + bool Set(const FilePath& path) WARN_UNUSED_RESULT; + + // Deletes the temporary directory wrapped by this object. + bool Delete() WARN_UNUSED_RESULT; + + // Caller takes ownership of the temporary directory so it won't be destroyed + // when this object goes out of scope. + FilePath Take(); + + // Returns the path to the created directory. Call one of the + // CreateUniqueTempDir* methods before getting the path. + const FilePath& GetPath() const; + + // Returns true if path_ is non-empty and exists. + bool IsValid() const; + + // Returns the prefix used for temp directory names generated by + // ScopedTempDirs. + static const FilePath::CharType* GetTempDirPrefix(); + + private: + FilePath path_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTempDir); +}; + +} // namespace base + +#endif // BASE_FILES_SCOPED_TEMP_DIR_H_ diff --git a/src/3rdparty/gn/base/gtest_prod_util.h b/src/3rdparty/gn/base/gtest_prod_util.h new file mode 100644 index 00000000000..664dded781f --- /dev/null +++ b/src/3rdparty/gn/base/gtest_prod_util.h @@ -0,0 +1,16 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GTEST_PROD_UTIL_H_ +#define BASE_GTEST_PROD_UTIL_H_ + +// TODO: Remove me. +#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \ + friend class test_case_name##test_name + +// TODO: Remove me. +#define FORWARD_DECLARE_TEST(test_case_name, test_name) \ + class test_case_name##test_name + +#endif // BASE_GTEST_PROD_UTIL_H_ diff --git a/src/3rdparty/gn/base/json/json_parser.cc b/src/3rdparty/gn/base/json/json_parser.cc new file mode 100644 index 00000000000..713f69223b1 --- /dev/null +++ b/src/3rdparty/gn/base/json/json_parser.cc @@ -0,0 +1,747 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/json_parser.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversion_utils.h" +#include "base/strings/utf_string_conversions.h" +#include "base/third_party/icu/icu_utf.h" +#include "base/values.h" + +namespace base { +namespace internal { + +namespace { + +const int32_t kExtendedASCIIStart = 0x80; + +// Simple class that checks for maximum recursion/"stack overflow." +class StackMarker { + public: + StackMarker(int max_depth, int* depth) + : max_depth_(max_depth), depth_(depth) { + ++(*depth_); + DCHECK_LE(*depth_, max_depth_); + } + ~StackMarker() { --(*depth_); } + + bool IsTooDeep() const { return *depth_ >= max_depth_; } + + private: + const int max_depth_; + int* const depth_; + + DISALLOW_COPY_AND_ASSIGN(StackMarker); +}; + +constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD; + +} // namespace + +// This is U+FFFD. +const char kUnicodeReplacementString[] = "\xEF\xBF\xBD"; + +JSONParser::JSONParser(int options, int max_depth) + : options_(options), + max_depth_(max_depth), + index_(0), + stack_depth_(0), + line_number_(0), + index_last_line_(0), + error_code_(JSONReader::JSON_NO_ERROR), + error_line_(0), + error_column_(0) { + CHECK_LE(max_depth, JSONReader::kStackMaxDepth); +} + +JSONParser::~JSONParser() = default; + +Optional JSONParser::Parse(StringPiece input) { + input_ = input; + index_ = 0; + line_number_ = 1; + index_last_line_ = 0; + + error_code_ = JSONReader::JSON_NO_ERROR; + error_line_ = 0; + error_column_ = 0; + + // ICU and ReadUnicodeCharacter() use int32_t for lengths, so ensure + // that the index_ will not overflow when parsing. + if (!base::IsValueInRangeForNumericType(input.length())) { + ReportError(JSONReader::JSON_TOO_LARGE, 0); + return nullopt; + } + + // When the input JSON string starts with a UTF-8 Byte-Order-Mark, + // advance the start position to avoid the ParseNextToken function mis- + // treating a Unicode BOM as an invalid character and returning NULL. + ConsumeIfMatch("\xEF\xBB\xBF"); + + // Parse the first and any nested tokens. + Optional root(ParseNextToken()); + if (!root) + return nullopt; + + // Make sure the input stream is at an end. + if (GetNextToken() != T_END_OF_INPUT) { + ReportError(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, 1); + return nullopt; + } + + return root; +} + +JSONReader::JsonParseError JSONParser::error_code() const { + return error_code_; +} + +std::string JSONParser::GetErrorMessage() const { + return FormatErrorMessage(error_line_, error_column_, + JSONReader::ErrorCodeToString(error_code_)); +} + +int JSONParser::error_line() const { + return error_line_; +} + +int JSONParser::error_column() const { + return error_column_; +} + +// StringBuilder /////////////////////////////////////////////////////////////// + +JSONParser::StringBuilder::StringBuilder() : StringBuilder(nullptr) {} + +JSONParser::StringBuilder::StringBuilder(const char* pos) + : pos_(pos), length_(0) {} + +JSONParser::StringBuilder::~StringBuilder() = default; + +JSONParser::StringBuilder& JSONParser::StringBuilder::operator=( + StringBuilder&& other) = default; + +void JSONParser::StringBuilder::Append(uint32_t point) { + DCHECK(IsValidCharacter(point)); + + if (point < kExtendedASCIIStart && !string_) { + DCHECK_EQ(static_cast(point), pos_[length_]); + ++length_; + } else { + Convert(); + if (UNLIKELY(point == kUnicodeReplacementPoint)) { + string_->append(kUnicodeReplacementString); + } else { + WriteUnicodeCharacter(point, &*string_); + } + } +} + +void JSONParser::StringBuilder::Convert() { + if (string_) + return; + string_.emplace(pos_, length_); +} + +std::string JSONParser::StringBuilder::DestructiveAsString() { + if (string_) + return std::move(*string_); + return std::string(pos_, length_); +} + +// JSONParser private ////////////////////////////////////////////////////////// + +Optional JSONParser::PeekChars(int count) { + if (static_cast(index_) + count > input_.length()) + return nullopt; + // Using StringPiece::substr() is significantly slower (according to + // base_perftests) than constructing a substring manually. + return StringPiece(input_.data() + index_, count); +} + +Optional JSONParser::PeekChar() { + Optional chars = PeekChars(1); + if (chars) + return (*chars)[0]; + return nullopt; +} + +Optional JSONParser::ConsumeChars(int count) { + Optional chars = PeekChars(count); + if (chars) + index_ += count; + return chars; +} + +Optional JSONParser::ConsumeChar() { + Optional chars = ConsumeChars(1); + if (chars) + return (*chars)[0]; + return nullopt; +} + +const char* JSONParser::pos() { + CHECK_LE(static_cast(index_), input_.length()); + return input_.data() + index_; +} + +JSONParser::Token JSONParser::GetNextToken() { + EatWhitespaceAndComments(); + + Optional c = PeekChar(); + if (!c) + return T_END_OF_INPUT; + + switch (*c) { + case '{': + return T_OBJECT_BEGIN; + case '}': + return T_OBJECT_END; + case '[': + return T_ARRAY_BEGIN; + case ']': + return T_ARRAY_END; + case '"': + return T_STRING; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return T_NUMBER; + case 't': + return T_BOOL_TRUE; + case 'f': + return T_BOOL_FALSE; + case 'n': + return T_NULL; + case ',': + return T_LIST_SEPARATOR; + case ':': + return T_OBJECT_PAIR_SEPARATOR; + default: + return T_INVALID_TOKEN; + } +} + +void JSONParser::EatWhitespaceAndComments() { + while (Optional c = PeekChar()) { + switch (*c) { + case '\r': + case '\n': + index_last_line_ = index_; + // Don't increment line_number_ twice for "\r\n". + if (!(c == '\n' && index_ > 0 && input_[index_ - 1] == '\r')) { + ++line_number_; + } + FALLTHROUGH; + case ' ': + case '\t': + ConsumeChar(); + break; + case '/': + if (!EatComment()) + return; + break; + default: + return; + } + } +} + +bool JSONParser::EatComment() { + Optional comment_start = ConsumeChars(2); + if (!comment_start) + return false; + + if (comment_start == "//") { + // Single line comment, read to newline. + while (Optional c = PeekChar()) { + if (c == '\n' || c == '\r') + return true; + ConsumeChar(); + } + } else if (comment_start == "/*") { + char previous_char = '\0'; + // Block comment, read until end marker. + while (Optional c = PeekChar()) { + if (previous_char == '*' && c == '/') { + // EatWhitespaceAndComments will inspect pos(), which will still be on + // the last / of the comment, so advance once more (which may also be + // end of input). + ConsumeChar(); + return true; + } + previous_char = *ConsumeChar(); + } + + // If the comment is unterminated, GetNextToken will report T_END_OF_INPUT. + } + + return false; +} + +Optional JSONParser::ParseNextToken() { + return ParseToken(GetNextToken()); +} + +Optional JSONParser::ParseToken(Token token) { + switch (token) { + case T_OBJECT_BEGIN: + return ConsumeDictionary(); + case T_ARRAY_BEGIN: + return ConsumeList(); + case T_STRING: + return ConsumeString(); + case T_NUMBER: + return ConsumeNumber(); + case T_BOOL_TRUE: + case T_BOOL_FALSE: + case T_NULL: + return ConsumeLiteral(); + default: + ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); + return nullopt; + } +} + +Optional JSONParser::ConsumeDictionary() { + if (ConsumeChar() != '{') { + ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); + return nullopt; + } + + StackMarker depth_check(max_depth_, &stack_depth_); + if (depth_check.IsTooDeep()) { + ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0); + return nullopt; + } + + std::vector dict_storage; + + Token token = GetNextToken(); + while (token != T_OBJECT_END) { + if (token != T_STRING) { + ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1); + return nullopt; + } + + // First consume the key. + StringBuilder key; + if (!ConsumeStringRaw(&key)) { + return nullopt; + } + + // Read the separator. + token = GetNextToken(); + if (token != T_OBJECT_PAIR_SEPARATOR) { + ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); + return nullopt; + } + + // The next token is the value. Ownership transfers to |dict|. + ConsumeChar(); + Optional value = ParseNextToken(); + if (!value) { + // ReportError from deeper level. + return nullopt; + } + + dict_storage.emplace_back(key.DestructiveAsString(), + std::make_unique(std::move(*value))); + + token = GetNextToken(); + if (token == T_LIST_SEPARATOR) { + ConsumeChar(); + token = GetNextToken(); + if (token == T_OBJECT_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) { + ReportError(JSONReader::JSON_TRAILING_COMMA, 1); + return nullopt; + } + } else if (token != T_OBJECT_END) { + ReportError(JSONReader::JSON_SYNTAX_ERROR, 0); + return nullopt; + } + } + + ConsumeChar(); // Closing '}'. + + return Value(Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES)); +} + +Optional JSONParser::ConsumeList() { + if (ConsumeChar() != '[') { + ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); + return nullopt; + } + + StackMarker depth_check(max_depth_, &stack_depth_); + if (depth_check.IsTooDeep()) { + ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0); + return nullopt; + } + + Value::ListStorage list_storage; + + Token token = GetNextToken(); + while (token != T_ARRAY_END) { + Optional item = ParseToken(token); + if (!item) { + // ReportError from deeper level. + return nullopt; + } + + list_storage.push_back(std::move(*item)); + + token = GetNextToken(); + if (token == T_LIST_SEPARATOR) { + ConsumeChar(); + token = GetNextToken(); + if (token == T_ARRAY_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) { + ReportError(JSONReader::JSON_TRAILING_COMMA, 1); + return nullopt; + } + } else if (token != T_ARRAY_END) { + ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); + return nullopt; + } + } + + ConsumeChar(); // Closing ']'. + + return Value(std::move(list_storage)); +} + +Optional JSONParser::ConsumeString() { + StringBuilder string; + if (!ConsumeStringRaw(&string)) + return nullopt; + + return Value(string.DestructiveAsString()); +} + +bool JSONParser::ConsumeStringRaw(StringBuilder* out) { + if (ConsumeChar() != '"') { + ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); + return false; + } + + // StringBuilder will internally build a StringPiece unless a UTF-16 + // conversion occurs, at which point it will perform a copy into a + // std::string. + StringBuilder string(pos()); + + while (PeekChar()) { + uint32_t next_char = 0; + if (!ReadUnicodeCharacter(input_.data(), + static_cast(input_.length()), &index_, + &next_char) || + !IsValidCharacter(next_char)) { + if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) { + ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1); + return false; + } + ConsumeChar(); + string.Append(kUnicodeReplacementPoint); + continue; + } + + if (next_char == '"') { + ConsumeChar(); + *out = std::move(string); + return true; + } else if (next_char != '\\') { + // If this character is not an escape sequence... + ConsumeChar(); + string.Append(next_char); + } else { + // And if it is an escape sequence, the input string will be adjusted + // (either by combining the two characters of an encoded escape sequence, + // or with a UTF conversion), so using StringPiece isn't possible -- force + // a conversion. + string.Convert(); + + // Read past the escape '\' and ensure there's a character following. + Optional escape_sequence = ConsumeChars(2); + if (!escape_sequence) { + ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); + return false; + } + + switch ((*escape_sequence)[1]) { + // Allowed esape sequences: + case 'x': { // UTF-8 sequence. + // UTF-8 \x escape sequences are not allowed in the spec, but they + // are supported here for backwards-compatiblity with the old parser. + escape_sequence = ConsumeChars(2); + if (!escape_sequence) { + ReportError(JSONReader::JSON_INVALID_ESCAPE, -2); + return false; + } + + int hex_digit = 0; + if (!HexStringToInt(*escape_sequence, &hex_digit) || + !IsValidCharacter(hex_digit)) { + ReportError(JSONReader::JSON_INVALID_ESCAPE, -2); + return false; + } + + string.Append(hex_digit); + break; + } + case 'u': { // UTF-16 sequence. + // UTF units are of the form \uXXXX. + uint32_t code_point; + if (!DecodeUTF16(&code_point)) { + ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); + return false; + } + string.Append(code_point); + break; + } + case '"': + string.Append('"'); + break; + case '\\': + string.Append('\\'); + break; + case '/': + string.Append('/'); + break; + case 'b': + string.Append('\b'); + break; + case 'f': + string.Append('\f'); + break; + case 'n': + string.Append('\n'); + break; + case 'r': + string.Append('\r'); + break; + case 't': + string.Append('\t'); + break; + case 'v': // Not listed as valid escape sequence in the RFC. + string.Append('\v'); + break; + // All other escape squences are illegal. + default: + ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); + return false; + } + } + } + + ReportError(JSONReader::JSON_SYNTAX_ERROR, 0); + return false; +} + +// Entry is at the first X in \uXXXX. +bool JSONParser::DecodeUTF16(uint32_t* out_code_point) { + Optional escape_sequence = ConsumeChars(4); + if (!escape_sequence) + return false; + + // Consume the UTF-16 code unit, which may be a high surrogate. + int code_unit16_high = 0; + if (!HexStringToInt(*escape_sequence, &code_unit16_high)) + return false; + + // If this is a high surrogate, consume the next code unit to get the + // low surrogate. + if (CBU16_IS_SURROGATE(code_unit16_high)) { + // Make sure this is the high surrogate. If not, it's an encoding + // error. + if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high)) + return false; + + // Make sure that the token has more characters to consume the + // lower surrogate. + if (!ConsumeIfMatch("\\u")) + return false; + + escape_sequence = ConsumeChars(4); + if (!escape_sequence) + return false; + + int code_unit16_low = 0; + if (!HexStringToInt(*escape_sequence, &code_unit16_low)) + return false; + + if (!CBU16_IS_TRAIL(code_unit16_low)) + return false; + + uint32_t code_point = + CBU16_GET_SUPPLEMENTARY(code_unit16_high, code_unit16_low); + if (!IsValidCharacter(code_point)) + return false; + + *out_code_point = code_point; + } else { + // Not a surrogate. + DCHECK(CBU16_IS_SINGLE(code_unit16_high)); + if (!IsValidCharacter(code_unit16_high)) { + if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) { + return false; + } + *out_code_point = kUnicodeReplacementPoint; + return true; + } + + *out_code_point = code_unit16_high; + } + + return true; +} + +Optional JSONParser::ConsumeNumber() { + const char* num_start = pos(); + const int start_index = index_; + int end_index = start_index; + + if (PeekChar() == '-') + ConsumeChar(); + + if (!ReadInt(false)) { + ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); + return nullopt; + } + end_index = index_; + + // The optional fraction part. + if (PeekChar() == '.') { + ConsumeChar(); + if (!ReadInt(true)) { + ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); + return nullopt; + } + end_index = index_; + } + + // Optional exponent part. + Optional c = PeekChar(); + if (c == 'e' || c == 'E') { + ConsumeChar(); + if (PeekChar() == '-' || PeekChar() == '+') { + ConsumeChar(); + } + if (!ReadInt(true)) { + ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); + return nullopt; + } + end_index = index_; + } + + // ReadInt is greedy because numbers have no easily detectable sentinel, + // so save off where the parser should be on exit (see Consume invariant at + // the top of the header), then make sure the next token is one which is + // valid. + int exit_index = index_; + + switch (GetNextToken()) { + case T_OBJECT_END: + case T_ARRAY_END: + case T_LIST_SEPARATOR: + case T_END_OF_INPUT: + break; + default: + ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); + return nullopt; + } + + index_ = exit_index; + + StringPiece num_string(num_start, end_index - start_index); + + int num_int; + if (StringToInt(num_string, &num_int)) + return Value(num_int); + + return nullopt; +} + +bool JSONParser::ReadInt(bool allow_leading_zeros) { + size_t len = 0; + char first = 0; + + while (Optional c = PeekChar()) { + if (!IsAsciiDigit(c)) + break; + + if (len == 0) + first = *c; + + ++len; + ConsumeChar(); + } + + if (len == 0) + return false; + + if (!allow_leading_zeros && len > 1 && first == '0') + return false; + + return true; +} + +Optional JSONParser::ConsumeLiteral() { + if (ConsumeIfMatch("true")) { + return Value(true); + } else if (ConsumeIfMatch("false")) { + return Value(false); + } else if (ConsumeIfMatch("null")) { + return Value(Value::Type::NONE); + } else { + ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); + return nullopt; + } +} + +bool JSONParser::ConsumeIfMatch(StringPiece match) { + if (match == PeekChars(match.size())) { + ConsumeChars(match.size()); + return true; + } + return false; +} + +void JSONParser::ReportError(JSONReader::JsonParseError code, + int column_adjust) { + error_code_ = code; + error_line_ = line_number_; + error_column_ = index_ - index_last_line_ + column_adjust; +} + +// static +std::string JSONParser::FormatErrorMessage(int line, + int column, + const std::string& description) { + if (line || column) { + return StringPrintf("Line: %i, column: %i, %s", line, column, + description.c_str()); + } + return description; +} + +} // namespace internal +} // namespace base diff --git a/src/3rdparty/gn/base/json/json_parser.h b/src/3rdparty/gn/base/json/json_parser.h new file mode 100644 index 00000000000..1289560983b --- /dev/null +++ b/src/3rdparty/gn/base/json/json_parser.h @@ -0,0 +1,260 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_JSON_JSON_PARSER_H_ +#define BASE_JSON_JSON_PARSER_H_ + +#include +#include + +#include +#include + +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/json/json_reader.h" +#include "base/macros.h" +#include "base/optional.h" +#include "base/strings/string_piece.h" + +namespace base { + +class Value; + +namespace internal { + +class JSONParserTest; + +// The implementation behind the JSONReader interface. This class is not meant +// to be used directly; it encapsulates logic that need not be exposed publicly. +// +// This parser guarantees O(n) time through the input string. Iteration happens +// on the byte level, with the functions ConsumeChars() and ConsumeChar(). The +// conversion from byte to JSON token happens without advancing the parser in +// GetNextToken/ParseToken, that is tokenization operates on the current parser +// position without advancing. +// +// Built on top of these are a family of Consume functions that iterate +// internally. Invariant: on entry of a Consume function, the parser is wound +// to the first byte of a valid JSON token. On exit, it is on the first byte +// after the token that was just consumed, which would likely be the first byte +// of the next token. +class JSONParser { + public: + JSONParser(int options, int max_depth = JSONReader::kStackMaxDepth); + ~JSONParser(); + + // Parses the input string according to the set options and returns the + // result as a Value. + // Wrap this in base::FooValue::From() to check the Value is of type Foo and + // convert to a FooValue at the same time. + Optional Parse(StringPiece input); + + // Returns the error code. + JSONReader::JsonParseError error_code() const; + + // Returns the human-friendly error message. + std::string GetErrorMessage() const; + + // Returns the error line number if parse error happened. Otherwise always + // returns 0. + int error_line() const; + + // Returns the error column number if parse error happened. Otherwise always + // returns 0. + int error_column() const; + + private: + enum Token { + T_OBJECT_BEGIN, // { + T_OBJECT_END, // } + T_ARRAY_BEGIN, // [ + T_ARRAY_END, // ] + T_STRING, + T_NUMBER, + T_BOOL_TRUE, // true + T_BOOL_FALSE, // false + T_NULL, // null + T_LIST_SEPARATOR, // , + T_OBJECT_PAIR_SEPARATOR, // : + T_END_OF_INPUT, + T_INVALID_TOKEN, + }; + + // A helper class used for parsing strings. One optimization performed is to + // create base::Value with a StringPiece to avoid unnecessary std::string + // copies. This is not possible if the input string needs to be decoded from + // UTF-16 to UTF-8, or if an escape sequence causes characters to be skipped. + // This class centralizes that logic. + class StringBuilder { + public: + // Empty constructor. Used for creating a builder with which to assign to. + StringBuilder(); + + // |pos| is the beginning of an input string, excluding the |"|. + explicit StringBuilder(const char* pos); + + ~StringBuilder(); + + StringBuilder& operator=(StringBuilder&& other); + + // Appends the Unicode code point |point| to the string, either by + // increasing the |length_| of the string if the string has not been + // converted, or by appending the UTF8 bytes for the code point. + void Append(uint32_t point); + + // Converts the builder from its default StringPiece to a full std::string, + // performing a copy. Once a builder is converted, it cannot be made a + // StringPiece again. + void Convert(); + + // Returns the builder as a string, invalidating all state. This allows + // the internal string buffer representation to be destructively moved + // in cases where the builder will not be needed any more. + std::string DestructiveAsString(); + + private: + // The beginning of the input string. + const char* pos_; + + // Number of bytes in |pos_| that make up the string being built. + size_t length_; + + // The copied string representation. Will be unset until Convert() is + // called. + base::Optional string_; + }; + + // Returns the next |count| bytes of the input stream, or nullopt if fewer + // than |count| bytes remain. + Optional PeekChars(int count); + + // Calls PeekChars() with a |count| of 1. + Optional PeekChar(); + + // Returns the next |count| bytes of the input stream, or nullopt if fewer + // than |count| bytes remain, and advances the parser position by |count|. + Optional ConsumeChars(int count); + + // Calls ConsumeChars() with a |count| of 1. + Optional ConsumeChar(); + + // Returns a pointer to the current character position. + const char* pos(); + + // Skips over whitespace and comments to find the next token in the stream. + // This does not advance the parser for non-whitespace or comment chars. + Token GetNextToken(); + + // Consumes whitespace characters and comments until the next non-that is + // encountered. + void EatWhitespaceAndComments(); + // Helper function that consumes a comment, assuming that the parser is + // currently wound to a '/'. + bool EatComment(); + + // Calls GetNextToken() and then ParseToken(). + Optional ParseNextToken(); + + // Takes a token that represents the start of a Value ("a structural token" + // in RFC terms) and consumes it, returning the result as a Value. + Optional ParseToken(Token token); + + // Assuming that the parser is currently wound to '{', this parses a JSON + // object into a Value. + Optional ConsumeDictionary(); + + // Assuming that the parser is wound to '[', this parses a JSON list into a + // Value. + Optional ConsumeList(); + + // Calls through ConsumeStringRaw and wraps it in a value. + Optional ConsumeString(); + + // Assuming that the parser is wound to a double quote, this parses a string, + // decoding any escape sequences and converts UTF-16 to UTF-8. Returns true on + // success and places result into |out|. Returns false on failure with + // error information set. + bool ConsumeStringRaw(StringBuilder* out); + // Helper function for ConsumeStringRaw() that consumes the next four or 10 + // bytes (parser is wound to the first character of a HEX sequence, with the + // potential for consuming another \uXXXX for a surrogate). Returns true on + // success and places the code point |out_code_point|, and false on failure. + bool DecodeUTF16(uint32_t* out_code_point); + + // Assuming that the parser is wound to the start of a valid JSON number, + // this parses and converts it to either an int or double value. + Optional ConsumeNumber(); + // Helper that reads characters that are ints. Returns true if a number was + // read and false on error. + bool ReadInt(bool allow_leading_zeros); + + // Consumes the literal values of |true|, |false|, and |null|, assuming the + // parser is wound to the first character of any of those. + Optional ConsumeLiteral(); + + // Helper function that returns true if the byte squence |match| can be + // consumed at the current parser position. Returns false if there are fewer + // than |match|-length bytes or if the sequence does not match, and the + // parser state is unchanged. + bool ConsumeIfMatch(StringPiece match); + + // Sets the error information to |code| at the current column, based on + // |index_| and |index_last_line_|, with an optional positive/negative + // adjustment by |column_adjust|. + void ReportError(JSONReader::JsonParseError code, int column_adjust); + + // Given the line and column number of an error, formats one of the error + // message contants from json_reader.h for human display. + static std::string FormatErrorMessage(int line, + int column, + const std::string& description); + + // base::JSONParserOptions that control parsing. + const int options_; + + // Maximum depth to parse. + const int max_depth_; + + // The input stream being parsed. Note: Not guaranteed to NUL-terminated. + StringPiece input_; + + // The index in the input stream to which the parser is wound. + int index_; + + // The number of times the parser has recursed (current stack depth). + int stack_depth_; + + // The line number that the parser is at currently. + int line_number_; + + // The last value of |index_| on the previous line. + int index_last_line_; + + // Error information. + JSONReader::JsonParseError error_code_; + int error_line_; + int error_column_; + + friend class JSONParserTest; + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, NextChar); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeDictionary); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeList); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeString); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidCharacters); + FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidUTF16EscapeSequence); + + DISALLOW_COPY_AND_ASSIGN(JSONParser); +}; + +// Used when decoding and an invalid utf-8 sequence is encountered. +extern const char kUnicodeReplacementString[]; + +} // namespace internal +} // namespace base + +#endif // BASE_JSON_JSON_PARSER_H_ diff --git a/src/3rdparty/gn/base/json/json_reader.cc b/src/3rdparty/gn/base/json/json_reader.cc new file mode 100644 index 00000000000..3d5475e8065 --- /dev/null +++ b/src/3rdparty/gn/base/json/json_reader.cc @@ -0,0 +1,119 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/json_reader.h" + +#include +#include + +#include "base/json/json_parser.h" +#include "base/logging.h" +#include "base/optional.h" +#include "base/values.h" + +namespace base { + +// Chosen to support 99.9% of documents found in the wild late 2016. +// http://crbug.com/673263 +const int JSONReader::kStackMaxDepth = 200; + +// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError. +static_assert(JSONReader::JSON_PARSE_ERROR_COUNT < 1000, + "JSONReader error out of bounds"); + +const char JSONReader::kInvalidEscape[] = "Invalid escape sequence."; +const char JSONReader::kSyntaxError[] = "Syntax error."; +const char JSONReader::kUnexpectedToken[] = "Unexpected token."; +const char JSONReader::kTrailingComma[] = "Trailing comma not allowed."; +const char JSONReader::kTooMuchNesting[] = "Too much nesting."; +const char JSONReader::kUnexpectedDataAfterRoot[] = + "Unexpected data after root element."; +const char JSONReader::kUnsupportedEncoding[] = + "Unsupported encoding. JSON must be UTF-8."; +const char JSONReader::kUnquotedDictionaryKey[] = + "Dictionary keys must be quoted."; +const char JSONReader::kInputTooLarge[] = "Input string is too large (>2GB)."; + +JSONReader::JSONReader(int options, int max_depth) + : parser_(new internal::JSONParser(options, max_depth)) {} + +JSONReader::~JSONReader() = default; + +// static +std::unique_ptr JSONReader::Read(StringPiece json, + int options, + int max_depth) { + internal::JSONParser parser(options, max_depth); + Optional root = parser.Parse(json); + return root ? std::make_unique(std::move(*root)) : nullptr; +} + +// static +std::unique_ptr JSONReader::ReadAndReturnError( + StringPiece json, + int options, + int* error_code_out, + std::string* error_msg_out, + int* error_line_out, + int* error_column_out) { + internal::JSONParser parser(options); + Optional root = parser.Parse(json); + if (!root) { + if (error_code_out) + *error_code_out = parser.error_code(); + if (error_msg_out) + *error_msg_out = parser.GetErrorMessage(); + if (error_line_out) + *error_line_out = parser.error_line(); + if (error_column_out) + *error_column_out = parser.error_column(); + } + + return root ? std::make_unique(std::move(*root)) : nullptr; +} + +// static +std::string JSONReader::ErrorCodeToString(JsonParseError error_code) { + switch (error_code) { + case JSON_NO_ERROR: + return std::string(); + case JSON_INVALID_ESCAPE: + return kInvalidEscape; + case JSON_SYNTAX_ERROR: + return kSyntaxError; + case JSON_UNEXPECTED_TOKEN: + return kUnexpectedToken; + case JSON_TRAILING_COMMA: + return kTrailingComma; + case JSON_TOO_MUCH_NESTING: + return kTooMuchNesting; + case JSON_UNEXPECTED_DATA_AFTER_ROOT: + return kUnexpectedDataAfterRoot; + case JSON_UNSUPPORTED_ENCODING: + return kUnsupportedEncoding; + case JSON_UNQUOTED_DICTIONARY_KEY: + return kUnquotedDictionaryKey; + case JSON_TOO_LARGE: + return kInputTooLarge; + case JSON_PARSE_ERROR_COUNT: + break; + } + NOTREACHED(); + return std::string(); +} + +std::unique_ptr JSONReader::ReadToValue(StringPiece json) { + Optional value = parser_->Parse(json); + return value ? std::make_unique(std::move(*value)) : nullptr; +} + +JSONReader::JsonParseError JSONReader::error_code() const { + return parser_->error_code(); +} + +std::string JSONReader::GetErrorMessage() const { + return parser_->GetErrorMessage(); +} + +} // namespace base diff --git a/src/3rdparty/gn/base/json/json_reader.h b/src/3rdparty/gn/base/json/json_reader.h new file mode 100644 index 00000000000..c4f7c8fc738 --- /dev/null +++ b/src/3rdparty/gn/base/json/json_reader.h @@ -0,0 +1,134 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A JSON parser. Converts strings of JSON into a Value object (see +// base/values.h). +// http://www.ietf.org/rfc/rfc4627.txt?number=4627 +// +// Known limitations/deviations from the RFC: +// - Only knows how to parse ints within the range of a signed 32 bit int and +// decimal numbers within a double. +// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16 +// (BE or LE) and UTF-32 (BE or LE) as well. +// - We limit nesting to 100 levels to prevent stack overflow (this is allowed +// by the RFC). +// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data +// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input +// UTF-8 string for the JSONReader::JsonToValue() function may start with a +// UTF-8 BOM (0xEF, 0xBB, 0xBF). +// To avoid the function from mis-treating a UTF-8 BOM as an invalid +// character, the function skips a Unicode BOM at the beginning of the +// Unicode string (converted from the input UTF-8 string) before parsing it. +// +// TODO(tc): Add a parsing option to to relax object keys being wrapped in +// double quotes +// TODO(tc): Add an option to disable comment stripping + +#ifndef BASE_JSON_JSON_READER_H_ +#define BASE_JSON_JSON_READER_H_ + +#include +#include + +#include "base/strings/string_piece.h" + +namespace base { + +class Value; + +namespace internal { +class JSONParser; +} + +enum JSONParserOptions { + // Parses the input strictly according to RFC 4627, except for where noted + // above. + JSON_PARSE_RFC = 0, + + // Allows commas to exist after the last element in structures. + JSON_ALLOW_TRAILING_COMMAS = 1 << 0, + + // If set the parser replaces invalid characters with the Unicode replacement + // character (U+FFFD). If not set, invalid characters trigger a hard error and + // parsing fails. + JSON_REPLACE_INVALID_CHARACTERS = 1 << 1, +}; + +class JSONReader { + public: + static const int kStackMaxDepth; + + // Error codes during parsing. + enum JsonParseError { + JSON_NO_ERROR = 0, + JSON_INVALID_ESCAPE, + JSON_SYNTAX_ERROR, + JSON_UNEXPECTED_TOKEN, + JSON_TRAILING_COMMA, + JSON_TOO_MUCH_NESTING, + JSON_UNEXPECTED_DATA_AFTER_ROOT, + JSON_UNSUPPORTED_ENCODING, + JSON_UNQUOTED_DICTIONARY_KEY, + JSON_TOO_LARGE, + JSON_PARSE_ERROR_COUNT + }; + + // String versions of parse error codes. + static const char kInvalidEscape[]; + static const char kSyntaxError[]; + static const char kUnexpectedToken[]; + static const char kTrailingComma[]; + static const char kTooMuchNesting[]; + static const char kUnexpectedDataAfterRoot[]; + static const char kUnsupportedEncoding[]; + static const char kUnquotedDictionaryKey[]; + static const char kInputTooLarge[]; + + // Constructs a reader. + JSONReader(int options = JSON_PARSE_RFC, int max_depth = kStackMaxDepth); + + ~JSONReader(); + + // Reads and parses |json|, returning a Value. + // If |json| is not a properly formed JSON string, returns nullptr. + // Wrap this in base::FooValue::From() to check the Value is of type Foo and + // convert to a FooValue at the same time. + static std::unique_ptr Read(StringPiece json, + int options = JSON_PARSE_RFC, + int max_depth = kStackMaxDepth); + + // Reads and parses |json| like Read(). |error_code_out| and |error_msg_out| + // are optional. If specified and nullptr is returned, they will be populated + // an error code and a formatted error message (including error location if + // appropriate). Otherwise, they will be unmodified. + static std::unique_ptr ReadAndReturnError( + StringPiece json, + int options, // JSONParserOptions + int* error_code_out, + std::string* error_msg_out, + int* error_line_out = nullptr, + int* error_column_out = nullptr); + + // Converts a JSON parse error code into a human readable message. + // Returns an empty string if error_code is JSON_NO_ERROR. + static std::string ErrorCodeToString(JsonParseError error_code); + + // Non-static version of Read() above. + std::unique_ptr ReadToValue(StringPiece json); + + // Returns the error code if the last call to ReadToValue() failed. + // Returns JSON_NO_ERROR otherwise. + JsonParseError error_code() const; + + // Converts error_code_ to a human-readable string, including line and column + // numbers if appropriate. + std::string GetErrorMessage() const; + + private: + std::unique_ptr parser_; +}; + +} // namespace base + +#endif // BASE_JSON_JSON_READER_H_ diff --git a/src/3rdparty/gn/base/json/json_value_converter.cc b/src/3rdparty/gn/base/json/json_value_converter.cc new file mode 100644 index 00000000000..55506e6b592 --- /dev/null +++ b/src/3rdparty/gn/base/json/json_value_converter.cc @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/json_value_converter.h" + +namespace base { +namespace internal { + +bool BasicValueConverter::Convert(const base::Value& value, + int* field) const { + return value.GetAsInteger(field); +} + +bool BasicValueConverter::Convert(const base::Value& value, + std::string* field) const { + return value.GetAsString(field); +} + +bool BasicValueConverter::Convert(const base::Value& value, + string16* field) const { + return value.GetAsString(field); +} + +bool BasicValueConverter::Convert(const base::Value& value, + double* field) const { + return value.GetAsDouble(field); +} + +bool BasicValueConverter::Convert(const base::Value& value, + bool* field) const { + return value.GetAsBoolean(field); +} + +} // namespace internal +} // namespace base diff --git a/src/3rdparty/gn/base/json/json_value_converter.h b/src/3rdparty/gn/base/json/json_value_converter.h new file mode 100644 index 00000000000..d91bead803f --- /dev/null +++ b/src/3rdparty/gn/base/json/json_value_converter.h @@ -0,0 +1,512 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_JSON_JSON_VALUE_CONVERTER_H_ +#define BASE_JSON_JSON_VALUE_CONVERTER_H_ + +#include + +#include +#include +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "base/values.h" + +// JSONValueConverter converts a JSON value into a C++ struct in a +// lightweight way. +// +// Usage: +// For real examples, you may want to refer to _unittest.cc file. +// +// Assume that you have a struct like this: +// struct Message { +// int foo; +// std::string bar; +// static void RegisterJSONConverter( +// JSONValueConverter* converter); +// }; +// +// And you want to parse a json data into this struct. First, you +// need to declare RegisterJSONConverter() method in your struct. +// // static +// void Message::RegisterJSONConverter( +// JSONValueConverter* converter) { +// converter->RegisterIntField("foo", &Message::foo); +// converter->RegisterStringField("bar", &Message::bar); +// } +// +// Then, you just instantiate your JSONValueConverter of your type and call +// Convert() method. +// Message message; +// JSONValueConverter converter; +// converter.Convert(json, &message); +// +// Convert() returns false when it fails. Here "fail" means that the value is +// structurally different from expected, such like a string value appears +// for an int field. Do not report failures for missing fields. +// Also note that Convert() will modify the passed |message| even when it +// fails for performance reason. +// +// For nested field, the internal message also has to implement the registration +// method. Then, just use RegisterNestedField() from the containing struct's +// RegisterJSONConverter method. +// struct Nested { +// Message foo; +// static void RegisterJSONConverter(...) { +// ... +// converter->RegisterNestedField("foo", &Nested::foo); +// } +// }; +// +// For repeated field, we just assume std::vector> +// for its container and you can put RegisterRepeatedInt or some other types. +// Use RegisterRepeatedMessage for nested repeated fields. +// +// Sometimes JSON format uses string representations for other types such +// like enum, timestamp, or URL. You can use RegisterCustomField method +// and specify a function to convert a StringPiece to your type. +// bool ConvertFunc(StringPiece s, YourEnum* result) { +// // do something and return true if succeed... +// } +// struct Message { +// YourEnum ye; +// ... +// static void RegisterJSONConverter(...) { +// ... +// converter->RegsiterCustomField( +// "your_enum", &Message::ye, &ConvertFunc); +// } +// }; + +namespace base { + +template +class JSONValueConverter; + +namespace internal { + +template +class FieldConverterBase { + public: + explicit FieldConverterBase(const std::string& path) : field_path_(path) {} + virtual ~FieldConverterBase() = default; + virtual bool ConvertField(const base::Value& value, + StructType* obj) const = 0; + const std::string& field_path() const { return field_path_; } + + private: + std::string field_path_; + DISALLOW_COPY_AND_ASSIGN(FieldConverterBase); +}; + +template +class ValueConverter { + public: + virtual ~ValueConverter() = default; + virtual bool Convert(const base::Value& value, FieldType* field) const = 0; +}; + +template +class FieldConverter : public FieldConverterBase { + public: + explicit FieldConverter(const std::string& path, + FieldType StructType::*field, + ValueConverter* converter) + : FieldConverterBase(path), + field_pointer_(field), + value_converter_(converter) {} + + bool ConvertField(const base::Value& value, StructType* dst) const override { + return value_converter_->Convert(value, &(dst->*field_pointer_)); + } + + private: + FieldType StructType::*field_pointer_; + std::unique_ptr> value_converter_; + DISALLOW_COPY_AND_ASSIGN(FieldConverter); +}; + +template +class BasicValueConverter; + +template <> +class BasicValueConverter : public ValueConverter { + public: + BasicValueConverter() = default; + + bool Convert(const base::Value& value, int* field) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); +}; + +template <> +class BasicValueConverter : public ValueConverter { + public: + BasicValueConverter() = default; + + bool Convert(const base::Value& value, std::string* field) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); +}; + +template <> +class BasicValueConverter : public ValueConverter { + public: + BasicValueConverter() = default; + + bool Convert(const base::Value& value, string16* field) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); +}; + +template <> +class BasicValueConverter : public ValueConverter { + public: + BasicValueConverter() = default; + + bool Convert(const base::Value& value, double* field) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); +}; + +template <> +class BasicValueConverter : public ValueConverter { + public: + BasicValueConverter() = default; + + bool Convert(const base::Value& value, bool* field) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); +}; + +template +class ValueFieldConverter : public ValueConverter { + public: + typedef bool (*ConvertFunc)(const base::Value* value, FieldType* field); + + explicit ValueFieldConverter(ConvertFunc convert_func) + : convert_func_(convert_func) {} + + bool Convert(const base::Value& value, FieldType* field) const override { + return convert_func_(&value, field); + } + + private: + ConvertFunc convert_func_; + + DISALLOW_COPY_AND_ASSIGN(ValueFieldConverter); +}; + +template +class CustomFieldConverter : public ValueConverter { + public: + typedef bool (*ConvertFunc)(StringPiece value, FieldType* field); + + explicit CustomFieldConverter(ConvertFunc convert_func) + : convert_func_(convert_func) {} + + bool Convert(const base::Value& value, FieldType* field) const override { + std::string string_value; + return value.GetAsString(&string_value) && + convert_func_(string_value, field); + } + + private: + ConvertFunc convert_func_; + + DISALLOW_COPY_AND_ASSIGN(CustomFieldConverter); +}; + +template +class NestedValueConverter : public ValueConverter { + public: + NestedValueConverter() = default; + + bool Convert(const base::Value& value, NestedType* field) const override { + return converter_.Convert(value, field); + } + + private: + JSONValueConverter converter_; + DISALLOW_COPY_AND_ASSIGN(NestedValueConverter); +}; + +template +class RepeatedValueConverter + : public ValueConverter>> { + public: + RepeatedValueConverter() = default; + + bool Convert(const base::Value& value, + std::vector>* field) const override { + const base::ListValue* list = NULL; + if (!value.GetAsList(&list)) { + // The field is not a list. + return false; + } + + field->reserve(list->GetSize()); + for (size_t i = 0; i < list->GetSize(); ++i) { + const base::Value* element = NULL; + if (!list->Get(i, &element)) + continue; + + std::unique_ptr e(new Element); + if (basic_converter_.Convert(*element, e.get())) { + field->push_back(std::move(e)); + } else { + return false; + } + } + return true; + } + + private: + BasicValueConverter basic_converter_; + DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter); +}; + +template +class RepeatedMessageConverter + : public ValueConverter>> { + public: + RepeatedMessageConverter() = default; + + bool Convert(const base::Value& value, + std::vector>* field) const override { + const base::ListValue* list = NULL; + if (!value.GetAsList(&list)) + return false; + + field->reserve(list->GetSize()); + for (size_t i = 0; i < list->GetSize(); ++i) { + const base::Value* element = NULL; + if (!list->Get(i, &element)) + continue; + + std::unique_ptr nested(new NestedType); + if (converter_.Convert(*element, nested.get())) { + field->push_back(std::move(nested)); + } else { + return false; + } + } + return true; + } + + private: + JSONValueConverter converter_; + DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter); +}; + +template +class RepeatedCustomValueConverter + : public ValueConverter>> { + public: + typedef bool (*ConvertFunc)(const base::Value* value, NestedType* field); + + explicit RepeatedCustomValueConverter(ConvertFunc convert_func) + : convert_func_(convert_func) {} + + bool Convert(const base::Value& value, + std::vector>* field) const override { + const base::ListValue* list = NULL; + if (!value.GetAsList(&list)) + return false; + + field->reserve(list->GetSize()); + for (size_t i = 0; i < list->GetSize(); ++i) { + const base::Value* element = NULL; + if (!list->Get(i, &element)) + continue; + + std::unique_ptr nested(new NestedType); + if ((*convert_func_)(element, nested.get())) { + field->push_back(std::move(nested)); + } else { + return false; + } + } + return true; + } + + private: + ConvertFunc convert_func_; + DISALLOW_COPY_AND_ASSIGN(RepeatedCustomValueConverter); +}; + +} // namespace internal + +template +class JSONValueConverter { + public: + JSONValueConverter() { StructType::RegisterJSONConverter(this); } + + void RegisterIntField(const std::string& field_name, int StructType::*field) { + fields_.push_back( + std::make_unique>( + field_name, field, new internal::BasicValueConverter)); + } + + void RegisterStringField(const std::string& field_name, + std::string StructType::*field) { + fields_.push_back( + std::make_unique>( + field_name, field, new internal::BasicValueConverter)); + } + + void RegisterStringField(const std::string& field_name, + string16 StructType::*field) { + fields_.push_back( + std::make_unique>( + field_name, field, new internal::BasicValueConverter)); + } + + void RegisterBoolField(const std::string& field_name, + bool StructType::*field) { + fields_.push_back( + std::make_unique>( + field_name, field, new internal::BasicValueConverter)); + } + + void RegisterDoubleField(const std::string& field_name, + double StructType::*field) { + fields_.push_back( + std::make_unique>( + field_name, field, new internal::BasicValueConverter)); + } + + template + void RegisterNestedField(const std::string& field_name, + NestedType StructType::*field) { + fields_.push_back( + std::make_unique>( + field_name, field, new internal::NestedValueConverter)); + } + + template + void RegisterCustomField(const std::string& field_name, + FieldType StructType::*field, + bool (*convert_func)(StringPiece, FieldType*)) { + fields_.push_back( + std::make_unique>( + field_name, field, + new internal::CustomFieldConverter(convert_func))); + } + + template + void RegisterCustomValueField(const std::string& field_name, + FieldType StructType::*field, + bool (*convert_func)(const base::Value*, + FieldType*)) { + fields_.push_back( + std::make_unique>( + field_name, field, + new internal::ValueFieldConverter(convert_func))); + } + + void RegisterRepeatedInt( + const std::string& field_name, + std::vector> StructType::*field) { + fields_.push_back(std::make_unique>>>( + field_name, field, new internal::RepeatedValueConverter)); + } + + void RegisterRepeatedString( + const std::string& field_name, + std::vector> StructType::*field) { + fields_.push_back( + std::make_unique>>>( + field_name, field, + new internal::RepeatedValueConverter)); + } + + void RegisterRepeatedString( + const std::string& field_name, + std::vector> StructType::*field) { + fields_.push_back(std::make_unique>>>( + field_name, field, new internal::RepeatedValueConverter)); + } + + void RegisterRepeatedDouble( + const std::string& field_name, + std::vector> StructType::*field) { + fields_.push_back(std::make_unique>>>( + field_name, field, new internal::RepeatedValueConverter)); + } + + void RegisterRepeatedBool( + const std::string& field_name, + std::vector> StructType::*field) { + fields_.push_back(std::make_unique>>>( + field_name, field, new internal::RepeatedValueConverter)); + } + + template + void RegisterRepeatedCustomValue( + const std::string& field_name, + std::vector> StructType::*field, + bool (*convert_func)(const base::Value*, NestedType*)) { + fields_.push_back( + std::make_unique>>>( + field_name, field, + new internal::RepeatedCustomValueConverter( + convert_func))); + } + + template + void RegisterRepeatedMessage( + const std::string& field_name, + std::vector> StructType::*field) { + fields_.push_back( + std::make_unique>>>( + field_name, field, + new internal::RepeatedMessageConverter)); + } + + bool Convert(const base::Value& value, StructType* output) const { + const DictionaryValue* dictionary_value = NULL; + if (!value.GetAsDictionary(&dictionary_value)) + return false; + + for (size_t i = 0; i < fields_.size(); ++i) { + const internal::FieldConverterBase* field_converter = + fields_[i].get(); + const base::Value* field = NULL; + if (dictionary_value->Get(field_converter->field_path(), &field)) { + if (!field_converter->ConvertField(*field, output)) { + return false; + } + } + } + return true; + } + + private: + std::vector>> + fields_; + + DISALLOW_COPY_AND_ASSIGN(JSONValueConverter); +}; + +} // namespace base + +#endif // BASE_JSON_JSON_VALUE_CONVERTER_H_ diff --git a/src/3rdparty/gn/base/json/json_writer.cc b/src/3rdparty/gn/base/json/json_writer.cc new file mode 100644 index 00000000000..656a87c22ee --- /dev/null +++ b/src/3rdparty/gn/base/json/json_writer.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/json_writer.h" + +#include + +#include +#include + +#include "base/json/string_escape.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "util/build_config.h" + +namespace base { + +#if defined(OS_WIN) +const char kPrettyPrintLineEnding[] = "\r\n"; +#else +const char kPrettyPrintLineEnding[] = "\n"; +#endif + +// static +bool JSONWriter::Write(const Value& node, std::string* json) { + return WriteWithOptions(node, 0, json); +} + +// static +bool JSONWriter::WriteWithOptions(const Value& node, + int options, + std::string* json) { + json->clear(); + // Is there a better way to estimate the size of the output? + json->reserve(1024); + + JSONWriter writer(options, json); + bool result = writer.BuildJSONString(node, 0U); + + if (options & OPTIONS_PRETTY_PRINT) + json->append(kPrettyPrintLineEnding); + + return result; +} + +JSONWriter::JSONWriter(int options, std::string* json) + : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0), + omit_double_type_preservation_( + (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0), + pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0), + json_string_(json) { + DCHECK(json); +} + +bool JSONWriter::BuildJSONString(const Value& node, size_t depth) { + switch (node.type()) { + case Value::Type::NONE: { + json_string_->append("null"); + return true; + } + + case Value::Type::BOOLEAN: { + bool value; + bool result = node.GetAsBoolean(&value); + DCHECK(result); + json_string_->append(value ? "true" : "false"); + return result; + } + + case Value::Type::INTEGER: { + int value; + bool result = node.GetAsInteger(&value); + DCHECK(result); + json_string_->append(IntToString(value)); + return result; + } + + case Value::Type::STRING: { + std::string value; + bool result = node.GetAsString(&value); + DCHECK(result); + EscapeJSONString(value, true, json_string_); + return result; + } + + case Value::Type::LIST: { + json_string_->push_back('['); + if (pretty_print_) + json_string_->push_back(' '); + + const ListValue* list = nullptr; + bool first_value_has_been_output = false; + bool result = node.GetAsList(&list); + DCHECK(result); + for (const auto& value : *list) { + if (omit_binary_values_ && value.type() == Value::Type::BINARY) + continue; + + if (first_value_has_been_output) { + json_string_->push_back(','); + if (pretty_print_) + json_string_->push_back(' '); + } + + if (!BuildJSONString(value, depth)) + result = false; + + first_value_has_been_output = true; + } + + if (pretty_print_) + json_string_->push_back(' '); + json_string_->push_back(']'); + return result; + } + + case Value::Type::DICTIONARY: { + json_string_->push_back('{'); + if (pretty_print_) + json_string_->append(kPrettyPrintLineEnding); + + const DictionaryValue* dict = nullptr; + bool first_value_has_been_output = false; + bool result = node.GetAsDictionary(&dict); + DCHECK(result); + for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd(); + itr.Advance()) { + if (omit_binary_values_ && itr.value().type() == Value::Type::BINARY) { + continue; + } + + if (first_value_has_been_output) { + json_string_->push_back(','); + if (pretty_print_) + json_string_->append(kPrettyPrintLineEnding); + } + + if (pretty_print_) + IndentLine(depth + 1U); + + EscapeJSONString(itr.key(), true, json_string_); + json_string_->push_back(':'); + if (pretty_print_) + json_string_->push_back(' '); + + if (!BuildJSONString(itr.value(), depth + 1U)) + result = false; + + first_value_has_been_output = true; + } + + if (pretty_print_) { + json_string_->append(kPrettyPrintLineEnding); + IndentLine(depth); + } + + json_string_->push_back('}'); + return result; + } + + case Value::Type::BINARY: + // Successful only if we're allowed to omit it. + DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value."; + return omit_binary_values_; + } + NOTREACHED(); + return false; +} + +void JSONWriter::IndentLine(size_t depth) { + json_string_->append(depth * 3U, ' '); +} + +} // namespace base diff --git a/src/3rdparty/gn/base/json/json_writer.h b/src/3rdparty/gn/base/json/json_writer.h new file mode 100644 index 00000000000..7edd3a68680 --- /dev/null +++ b/src/3rdparty/gn/base/json/json_writer.h @@ -0,0 +1,74 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_JSON_JSON_WRITER_H_ +#define BASE_JSON_JSON_WRITER_H_ + +#include + +#include + +#include "base/macros.h" + +namespace base { + +class Value; + +class JSONWriter { + public: + enum Options { + // This option instructs the writer that if a Binary value is encountered, + // the value (and key if within a dictionary) will be omitted from the + // output, and success will be returned. Otherwise, if a binary value is + // encountered, failure will be returned. + OPTIONS_OMIT_BINARY_VALUES = 1 << 0, + + // This option instructs the writer to write doubles that have no fractional + // part as a normal integer (i.e., without using exponential notation + // or appending a '.0') as long as the value is within the range of a + // 64-bit int. + OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1, + + // Return a slightly nicer formatted json string (pads with whitespace to + // help with readability). + OPTIONS_PRETTY_PRINT = 1 << 2, + }; + + // Given a root node, generates a JSON string and puts it into |json|. + // The output string is overwritten and not appended. + // + // TODO(tc): Should we generate json if it would be invalid json (e.g., + // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float + // values)? Return true on success and false on failure. + static bool Write(const Value& node, std::string* json); + + // Same as above but with |options| which is a bunch of JSONWriter::Options + // bitwise ORed together. Return true on success and false on failure. + static bool WriteWithOptions(const Value& node, + int options, + std::string* json); + + private: + JSONWriter(int options, std::string* json); + + // Called recursively to build the JSON string. When completed, + // |json_string_| will contain the JSON. + bool BuildJSONString(const Value& node, size_t depth); + + // Adds space to json_string_ for the indent level. + void IndentLine(size_t depth); + + bool omit_binary_values_; + bool omit_double_type_preservation_; + bool pretty_print_; + + // Where we write JSON data as we generate it. + std::string* json_string_; + + DISALLOW_COPY_AND_ASSIGN(JSONWriter); +}; + +} // namespace base + +#endif // BASE_JSON_JSON_WRITER_H_ diff --git a/src/3rdparty/gn/base/json/string_escape.cc b/src/3rdparty/gn/base/json/string_escape.cc new file mode 100644 index 00000000000..471a9d30cfa --- /dev/null +++ b/src/3rdparty/gn/base/json/string_escape.cc @@ -0,0 +1,167 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/string_escape.h" + +#include +#include + +#include +#include + +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversion_utils.h" +#include "base/strings/utf_string_conversions.h" +#include "base/third_party/icu/icu_utf.h" + +namespace base { + +namespace { + +// Format string for printing a \uXXXX escape sequence. +const char kU16EscapeFormat[] = "\\u%04X"; + +// The code point to output for an invalid input code unit. +const uint32_t kReplacementCodePoint = 0xFFFD; + +// Used below in EscapeSpecialCodePoint(). +static_assert('<' == 0x3C, "less than sign must be 0x3c"); + +// Try to escape the |code_point| if it is a known special character. If +// successful, returns true and appends the escape sequence to |dest|. This +// isn't required by the spec, but it's more readable by humans. +bool EscapeSpecialCodePoint(uint32_t code_point, std::string* dest) { + // WARNING: if you add a new case here, you need to update the reader as well. + // Note: \v is in the reader, but not here since the JSON spec doesn't + // allow it. + switch (code_point) { + case '\b': + dest->append("\\b"); + break; + case '\f': + dest->append("\\f"); + break; + case '\n': + dest->append("\\n"); + break; + case '\r': + dest->append("\\r"); + break; + case '\t': + dest->append("\\t"); + break; + case '\\': + dest->append("\\\\"); + break; + case '"': + dest->append("\\\""); + break; + // Escape < to prevent script execution; escaping > is not necessary and + // not doing so save a few bytes. + case '<': + dest->append("\\u003C"); + break; + // Escape the "Line Separator" and "Paragraph Separator" characters, since + // they should be treated like a new line \r or \n. + case 0x2028: + dest->append("\\u2028"); + break; + case 0x2029: + dest->append("\\u2029"); + break; + default: + return false; + } + return true; +} + +template +bool EscapeJSONStringImpl(const S& str, bool put_in_quotes, std::string* dest) { + bool did_replacement = false; + + if (put_in_quotes) + dest->push_back('"'); + + // Casting is necessary because ICU uses int32_t. Try and do so safely. + CHECK_LE(str.length(), + static_cast(std::numeric_limits::max())); + const int32_t length = static_cast(str.length()); + + for (int32_t i = 0; i < length; ++i) { + uint32_t code_point; + if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) || + code_point == static_cast(CBU_SENTINEL) || + !IsValidCharacter(code_point)) { + code_point = kReplacementCodePoint; + did_replacement = true; + } + + if (EscapeSpecialCodePoint(code_point, dest)) + continue; + + // Escape non-printing characters. + if (code_point < 32) + base::StringAppendF(dest, kU16EscapeFormat, code_point); + else + WriteUnicodeCharacter(code_point, dest); + } + + if (put_in_quotes) + dest->push_back('"'); + + return !did_replacement; +} + +} // namespace + +bool EscapeJSONString(StringPiece str, bool put_in_quotes, std::string* dest) { + return EscapeJSONStringImpl(str, put_in_quotes, dest); +} + +bool EscapeJSONString(StringPiece16 str, + bool put_in_quotes, + std::string* dest) { + return EscapeJSONStringImpl(str, put_in_quotes, dest); +} + +std::string GetQuotedJSONString(StringPiece str) { + std::string dest; + bool ok = EscapeJSONStringImpl(str, true, &dest); + DCHECK(ok); + return dest; +} + +std::string GetQuotedJSONString(StringPiece16 str) { + std::string dest; + bool ok = EscapeJSONStringImpl(str, true, &dest); + DCHECK(ok); + return dest; +} + +std::string EscapeBytesAsInvalidJSONString(StringPiece str, + bool put_in_quotes) { + std::string dest; + + if (put_in_quotes) + dest.push_back('"'); + + for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) { + unsigned char c = *it; + if (EscapeSpecialCodePoint(c, &dest)) + continue; + + if (c < 32 || c > 126) + base::StringAppendF(&dest, kU16EscapeFormat, c); + else + dest.push_back(*it); + } + + if (put_in_quotes) + dest.push_back('"'); + + return dest; +} + +} // namespace base diff --git a/src/3rdparty/gn/base/json/string_escape.h b/src/3rdparty/gn/base/json/string_escape.h new file mode 100644 index 00000000000..d318b6aa1c1 --- /dev/null +++ b/src/3rdparty/gn/base/json/string_escape.h @@ -0,0 +1,55 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file defines utility functions for escaping strings suitable for JSON. + +#ifndef BASE_JSON_STRING_ESCAPE_H_ +#define BASE_JSON_STRING_ESCAPE_H_ + +#include + +#include "base/strings/string_piece.h" + +namespace base { + +// Appends to |dest| an escaped version of |str|. Valid UTF-8 code units and +// characters will pass through from the input to the output. Invalid code +// units and characters will be replaced with the U+FFFD replacement character. +// This function returns true if no replacement was necessary and false if +// there was a lossy replacement. On return, |dest| will contain a valid UTF-8 +// JSON string. +// +// Non-printing control characters will be escaped as \uXXXX sequences for +// readability. +// +// If |put_in_quotes| is true, then a leading and trailing double-quote mark +// will be appended to |dest| as well. +bool EscapeJSONString(StringPiece str, bool put_in_quotes, std::string* dest); + +// Performs a similar function to the UTF-8 StringPiece version above, +// converting UTF-16 code units to UTF-8 code units and escaping non-printing +// control characters. On return, |dest| will contain a valid UTF-8 JSON string. +bool EscapeJSONString(StringPiece16 str, bool put_in_quotes, std::string* dest); + +// Helper functions that wrap the above two functions but return the value +// instead of appending. |put_in_quotes| is always true. +std::string GetQuotedJSONString(StringPiece str); +std::string GetQuotedJSONString(StringPiece16 str); + +// Given an arbitrary byte string |str|, this will escape all non-ASCII bytes +// as \uXXXX escape sequences. This function is *NOT* meant to be used with +// Unicode strings and does not validate |str| as one. +// +// CAVEAT CALLER: The output of this function may not be valid JSON, since +// JSON requires escape sequences to be valid UTF-16 code units. This output +// will be mangled if passed to to the base::JSONReader, since the reader will +// interpret it as UTF-16 and convert it to UTF-8. +// +// The output of this function takes the *appearance* of JSON but is not in +// fact valid according to RFC 4627. +std::string EscapeBytesAsInvalidJSONString(StringPiece str, bool put_in_quotes); + +} // namespace base + +#endif // BASE_JSON_STRING_ESCAPE_H_ diff --git a/src/3rdparty/gn/base/logging.cc b/src/3rdparty/gn/base/logging.cc new file mode 100644 index 00000000000..c2c243f38f0 --- /dev/null +++ b/src/3rdparty/gn/base/logging.cc @@ -0,0 +1,330 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" + +#include +#include + +#include + +#include "base/macros.h" +#include "util/build_config.h" + +#if defined(OS_WIN) +#include +#include +// Windows warns on using write(). It prefers _write(). +#define write(fd, buf, count) _write(fd, buf, static_cast(count)) +// Windows doesn't define STDERR_FILENO. Define it here. +#define STDERR_FILENO 2 + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "base/callback.h" +#include "base/containers/stack.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include "base/posix/safe_strerror.h" +#endif + +namespace logging { + +namespace { + +const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"}; +static_assert(LOG_NUM_SEVERITIES == arraysize(log_severity_names), + "Incorrect number of log_severity_names"); + +const char* log_severity_name(int severity) { + if (severity >= 0 && severity < LOG_NUM_SEVERITIES) + return log_severity_names[severity]; + return "UNKNOWN"; +} + +int g_min_log_level = 0; + +// For LOG_ERROR and above, always print to stderr. +const int kAlwaysPrintErrorLevel = LOG_ERROR; + +} // namespace + +#if DCHECK_IS_CONFIGURABLE +// In DCHECK-enabled Chrome builds, allow the meaning of LOG_DCHECK to be +// determined at run-time. We default it to INFO, to avoid it triggering +// crashes before the run-time has explicitly chosen the behaviour. +logging::LogSeverity LOG_DCHECK = LOG_INFO; +#endif // DCHECK_IS_CONFIGURABLE + +// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have +// an object of the correct type on the LHS of the unused part of the ternary +// operator. +std::ostream* g_swallow_stream; + +void SetMinLogLevel(int level) { + g_min_log_level = std::min(LOG_FATAL, level); +} + +int GetMinLogLevel() { + return g_min_log_level; +} + +bool ShouldCreateLogMessage(int severity) { + if (severity < g_min_log_level) + return false; + + // Return true here unless we know ~LogMessage won't do anything. Note that + // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even + // when g_logging_destination is LOG_NONE. + return severity >= kAlwaysPrintErrorLevel; +} + +// Explicit instantiations for commonly used comparisons. +template std::string* MakeCheckOpString(const int&, + const int&, + const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, + const unsigned long&, + const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, + const unsigned int&, + const char* names); +template std::string* MakeCheckOpString( + const unsigned int&, + const unsigned long&, + const char* names); +template std::string* MakeCheckOpString( + const std::string&, + const std::string&, + const char* name); + +void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) { + (*os) << "nullptr"; +} + +#if defined(OS_WIN) +LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) {} + +LogMessage::SaveLastError::~SaveLastError() { + ::SetLastError(last_error_); +} +#endif // defined(OS_WIN) + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity) + : severity_(severity), file_(file), line_(line) { + Init(file, line); +} + +LogMessage::LogMessage(const char* file, int line, const char* condition) + : severity_(LOG_FATAL), file_(file), line_(line) { + Init(file, line); + stream_ << "Check failed: " << condition << ". "; +} + +LogMessage::LogMessage(const char* file, int line, std::string* result) + : severity_(LOG_FATAL), file_(file), line_(line) { + Init(file, line); + stream_ << "Check failed: " << *result; + delete result; +} + +LogMessage::LogMessage(const char* file, + int line, + LogSeverity severity, + std::string* result) + : severity_(severity), file_(file), line_(line) { + Init(file, line); + stream_ << "Check failed: " << *result; + delete result; +} + +LogMessage::~LogMessage() { + if (severity_ == LOG_FATAL) { + stream_ << std::endl; // Newline to separate from log message. + } + stream_ << std::endl; + std::string str_newline(stream_.str()); + +#if defined(OS_WIN) + OutputDebugStringA(str_newline.c_str()); +#endif + ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); + fflush(stderr); + + if (severity_ == LOG_FATAL) { + abort(); + } +} + +// writes the common header info to the stream +void LogMessage::Init(const char* file, int line) { + base::StringPiece filename(file); + size_t last_slash_pos = filename.find_last_of("\\/"); + if (last_slash_pos != base::StringPiece::npos) + filename.remove_prefix(last_slash_pos + 1); + + // TODO(darin): It might be nice if the columns were fixed width. + + stream_ << '['; + stream_ << std::this_thread::get_id() << ':'; +#if defined(OS_WIN) + SYSTEMTIME local_time; + GetLocalTime(&local_time); + stream_ << std::setfill('0') << std::setw(2) << local_time.wMonth + << std::setw(2) << local_time.wDay << '/' << std::setw(2) + << local_time.wHour << std::setw(2) << local_time.wMinute + << std::setw(2) << local_time.wSecond << '.' << std::setw(3) + << local_time.wMilliseconds << ':'; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + timeval tv; + gettimeofday(&tv, nullptr); + time_t t = tv.tv_sec; + struct tm local_time; + localtime_r(&t, &local_time); + struct tm* tm_time = &local_time; + stream_ << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon + << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2) + << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2) + << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << ':'; +#else +#error Unsupported platform +#endif + if (severity_ >= 0) + stream_ << log_severity_name(severity_); + else + stream_ << "VERBOSE" << -severity_; + + stream_ << ":" << filename << "(" << line << ")] "; + + message_start_ = stream_.str().length(); +} + +#if defined(OS_WIN) +// This has already been defined in the header, but defining it again as DWORD +// ensures that the type used in the header is equivalent to DWORD. If not, +// the redefinition is a compile error. +typedef DWORD SystemErrorCode; +#endif + +SystemErrorCode GetLastSystemErrorCode() { +#if defined(OS_WIN) + return ::GetLastError(); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return errno; +#endif +} + +std::string SystemErrorCodeToString(SystemErrorCode error_code) { +#if defined(OS_WIN) + const int kErrorMessageBufferSize = 256; + char msgbuf[kErrorMessageBufferSize]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf, + arraysize(msgbuf), nullptr); + if (len) { + // Messages returned by system end with line breaks. + return base::CollapseWhitespaceASCII(msgbuf, true) + + base::StringPrintf(" (0x%lX)", error_code); + } + return base::StringPrintf("Error (0x%lX) while retrieving error. (0x%lX)", + GetLastError(), error_code); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return base::safe_strerror(error_code) + + base::StringPrintf(" (%d)", error_code); +#endif // defined(OS_WIN) +} + +#if defined(OS_WIN) +Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err) + : err_(err), log_message_(file, line, severity) {} + +Win32ErrorLogMessage::~Win32ErrorLogMessage() { + stream() << ": " << SystemErrorCodeToString(err_); +} +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +ErrnoLogMessage::ErrnoLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err) + : err_(err), log_message_(file, line, severity) {} + +ErrnoLogMessage::~ErrnoLogMessage() { + stream() << ": " << SystemErrorCodeToString(err_); +} +#endif // defined(OS_WIN) + +void RawLog(int level, const char* message) { + if (level >= g_min_log_level && message) { + size_t bytes_written = 0; + const size_t message_len = strlen(message); + int rv; + while (bytes_written < message_len) { + rv = HANDLE_EINTR(write(STDERR_FILENO, message + bytes_written, + message_len - bytes_written)); + if (rv < 0) { + // Give up, nothing we can do now. + break; + } + bytes_written += rv; + } + + if (message_len > 0 && message[message_len - 1] != '\n') { + do { + rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1)); + if (rv < 0) { + // Give up, nothing we can do now. + break; + } + } while (rv != 1); + } + } + + if (level == LOG_FATAL) + abort(); +} + +// This was defined at the beginning of this file. +#undef write + +void LogErrorNotReached(const char* file, int line) { + LogMessage(file, line, LOG_ERROR).stream() << "NOTREACHED() hit."; +} + +} // namespace logging + +std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr) { + return out << (wstr ? base::WideToUTF8(wstr) : std::string()); +} diff --git a/src/3rdparty/gn/base/logging.h b/src/3rdparty/gn/base/logging.h new file mode 100644 index 00000000000..2edad38c4a5 --- /dev/null +++ b/src/3rdparty/gn/base/logging.h @@ -0,0 +1,956 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOGGING_H_ +#define BASE_LOGGING_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string_piece_forward.h" +#include "base/template_util.h" +#include "util/build_config.h" + +// +// Optional message capabilities +// ----------------------------- +// Assertion failed messages and fatal errors are displayed in a dialog box +// before the application exits. However, running this UI creates a message +// loop, which causes application messages to be processed and potentially +// dispatched to existing application windows. Since the application is in a +// bad state when this assertion dialog is displayed, these messages may not +// get processed and hang the dialog, or the application might go crazy. +// +// Therefore, it can be beneficial to display the error dialog in a separate +// process from the main application. When the logging system needs to display +// a fatal error dialog box, it will look for a program called +// "DebugMessage.exe" in the same directory as the application executable. It +// will run this application with the message as the command line, and will +// not include the name of the application as is traditional for easier +// parsing. +// +// The code for DebugMessage.exe is only one line. In WinMain, do: +// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0); +// +// If DebugMessage.exe is not found, the logging code will use a normal +// MessageBox, potentially causing the problems discussed above. + +// Instructions +// ------------ +// +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// The CHECK(condition) macro is active in both debug and release builds and +// effectively performs a LOG(FATAL) which terminates the process and +// generates a crashdump unless a debugger is attached. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. LOG_IF and development flags also work well together +// because the code can be compiled away sometimes. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// We also override the standard 'assert' to use 'DLOG_ASSERT'. +// +// Lastly, there is: +// +// PLOG(ERROR) << "Couldn't do foo"; +// DPLOG(ERROR) << "Couldn't do foo"; +// PLOG_IF(ERROR, cond) << "Couldn't do foo"; +// DPLOG_IF(ERROR, cond) << "Couldn't do foo"; +// PCHECK(condition) << "Couldn't do foo"; +// DPCHECK(condition) << "Couldn't do foo"; +// +// which append the last system error to the message in string form (taken from +// GetLastError() on Windows and errno on POSIX). +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// There is the special severity of DFATAL, which logs FATAL in debug mode, +// ERROR in normal mode. + +namespace logging { + +// Sets the log level. Anything at or above this level will be written to the +// log file/displayed to the user (if applicable). Anything below this level +// will be silently ignored. The log level defaults to 0 (everything is logged +// up to level INFO) if this function is not called. +void SetMinLogLevel(int level); + +// Gets the current log level. +int GetMinLogLevel(); + +// Used by LOG_IS_ON to lazy-evaluate stream arguments. +bool ShouldCreateLogMessage(int severity); + +// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints +// to Clang which control what code paths are statically analyzed, +// and is meant to be used in conjunction with assert & assert-like functions. +// The expression is passed straight through if analysis isn't enabled. +// +// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current +// codepath and any other branching codepaths that might follow. +#if defined(__clang_analyzer__) + +inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) { + return false; +} + +inline constexpr bool AnalyzerAssumeTrue(bool arg) { + // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is + // false. + return arg || AnalyzerNoReturn(); +} + +#define ANALYZER_ASSUME_TRUE(arg) logging::AnalyzerAssumeTrue(!!(arg)) +#define ANALYZER_SKIP_THIS_PATH() \ + static_cast(::logging::AnalyzerNoReturn()) +#define ANALYZER_ALLOW_UNUSED(var) static_cast(var); + +#else // !defined(__clang_analyzer__) + +#define ANALYZER_ASSUME_TRUE(arg) (arg) +#define ANALYZER_SKIP_THIS_PATH() +#define ANALYZER_ALLOW_UNUSED(var) static_cast(var); + +#endif // defined(__clang_analyzer__) + +typedef int LogSeverity; +const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity +// Note: the log severities are used to index into the array of names, +// see log_severity_names. +const LogSeverity LOG_INFO = 0; +const LogSeverity LOG_WARNING = 1; +const LogSeverity LOG_ERROR = 2; +const LogSeverity LOG_FATAL = 3; +const LogSeverity LOG_NUM_SEVERITIES = 4; + +// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode +#if defined(NDEBUG) +const LogSeverity LOG_DFATAL = LOG_ERROR; +#else +const LogSeverity LOG_DFATAL = LOG_FATAL; +#endif + +// A few definitions of macros that don't generate much code. These are used +// by LOG() and LOG_IF, etc. Since these are used all over our code, it's +// better to have compact code for these operations. +#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_WARNING, \ + ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_ERROR, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DCHECK, ##__VA_ARGS__) + +#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) +#define COMPACT_GOOGLE_LOG_WARNING COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) +#define COMPACT_GOOGLE_LOG_ERROR COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) +#define COMPACT_GOOGLE_LOG_FATAL COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) +#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage) + +#if defined(OS_WIN) +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that +// the Windows SDK does for consistency. +#define ERROR 0 +#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +// Needed for LOG_IS_ON(ERROR). +const LogSeverity LOG_0 = LOG_ERROR; +#endif + +// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also, +// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will +// always fire if they fail. +#define LOG_IS_ON(severity) \ + (::logging::ShouldCreateLogMessage(::logging::LOG_##severity)) + +// Helper macro which avoids evaluating the arguments to a stream if +// the condition doesn't hold. Condition is evaluated once and only once. +#define LAZY_STREAM(stream, condition) \ + !(condition) ? (void)0 : ::logging::LogMessageVoidify() & (stream) + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_##severity.stream() + +#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity)) +#define LOG_IF(severity, condition) \ + LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \ + << "Assert failed: " #condition ". " + +#if defined(OS_WIN) +#define PLOG_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_##severity(Win32ErrorLogMessage, \ + ::logging::GetLastSystemErrorCode()) \ + .stream() +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#define PLOG_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_##severity(ErrnoLogMessage, \ + ::logging::GetLastSystemErrorCode()) \ + .stream() +#endif + +#define PLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity)) + +#define PLOG_IF(severity, condition) \ + LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +extern std::ostream* g_swallow_stream; + +// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to +// avoid the creation of an object with a non-trivial destructor (LogMessage). +// On MSVC x86 (checked on 2015 Update 3), this causes a few additional +// pointless instructions to be emitted even at full optimization level, even +// though the : arm of the ternary operator is clearly never executed. Using a +// simpler object to be &'d with Voidify() avoids these extra instructions. +// Using a simpler POD object with a templated operator<< also works to avoid +// these instructions. However, this causes warnings on statically defined +// implementations of operator<<(std::ostream, ...) in some .cc files, because +// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an +// ostream* also is not suitable, because some compilers warn of undefined +// behavior. +#define EAT_STREAM_PARAMETERS \ + true ? (void)0 \ + : ::logging::LogMessageVoidify() & (*::logging::g_swallow_stream) + +// Captures the result of a CHECK_EQ (for example) and facilitates testing as a +// boolean. +class CheckOpResult { + public: + // |message| must be non-null if and only if the check failed. + CheckOpResult(std::string* message) : message_(message) {} + // Returns true if the check succeeded. + operator bool() const { return !message_; } + // Returns the message. + std::string* message() { return message_; } + + private: + std::string* message_; +}; + +// Crashes in the fastest possible way with no attempt at logging. +// There are different constraints to satisfy here, see http://crbug.com/664209 +// for more context: +// - The trap instructions, and hence the PC value at crash time, have to be +// distinct and not get folded into the same opcode by the compiler. +// On Linux/Android this is tricky because GCC still folds identical +// asm volatile blocks. The workaround is generating distinct opcodes for +// each CHECK using the __COUNTER__ macro. +// - The debug info for the trap instruction has to be attributed to the source +// line that has the CHECK(), to make crash reports actionable. This rules +// out the ability of using a inline function, at least as long as clang +// doesn't support attribute(artificial). +// - Failed CHECKs should produce a signal that is distinguishable from an +// invalid memory access, to improve the actionability of crash reports. +// - The compiler should treat the CHECK as no-return instructions, so that the +// trap code can be efficiently packed in the prologue of the function and +// doesn't interfere with the main execution flow. +// - When debugging, developers shouldn't be able to accidentally step over a +// CHECK. This is achieved by putting opcodes that will cause a non +// continuable exception after the actual trap instruction. +// - Don't cause too much binary bloat. +#if defined(COMPILER_GCC) + +#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) +// int 3 will generate a SIGTRAP. +#define TRAP_SEQUENCE() \ + asm volatile( \ + "int3; ud2; push %0;" ::"i"(static_cast(__COUNTER__))) + +#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL) +// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running +// as a 32 bit userspace app on arm64. There doesn't seem to be any way to +// cause a SIGTRAP from userspace without using a syscall (which would be a +// problem for sandboxing). +#define TRAP_SEQUENCE() \ + asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256)) + +#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL) +// This will always generate a SIGTRAP on arm64. +#define TRAP_SEQUENCE() \ + asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536)) + +#else +// Crash report accuracy will not be guaranteed on other architectures, but at +// least this will crash as expected. +#define TRAP_SEQUENCE() __builtin_trap() +#endif // ARCH_CPU_* + +// CHECK() and the trap sequence can be invoked from a constexpr function. +// This could make compilation fail on GCC, as it forbids directly using inline +// asm inside a constexpr function. However, it allows calling a lambda +// expression including the same asm. +// The side effect is that the top of the stacktrace will not point to the +// calling function, but to this anonymous lambda. This is still useful as the +// full name of the lambda will typically include the name of the function that +// calls CHECK() and the debugger will still break at the right line of code. +#if !defined(__clang__) +#define WRAPPED_TRAP_SEQUENCE() \ + do { \ + [] { TRAP_SEQUENCE(); }(); \ + } while (false) +#else +#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE() +#endif + +#define IMMEDIATE_CRASH() \ + ({ \ + WRAPPED_TRAP_SEQUENCE(); \ + __builtin_unreachable(); \ + }) + +#elif defined(COMPILER_MSVC) + +// Clang is cleverer about coalescing int3s, so we need to add a unique-ish +// instruction following the __debugbreak() to have it emit distinct locations +// for CHECKs rather than collapsing them all together. It would be nice to use +// a short intrinsic to do this (and perhaps have only one implementation for +// both clang and MSVC), however clang-cl currently does not support intrinsics. +// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have +// two implementations. Normally clang-cl's version will be 5 bytes (1 for +// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg): +// https://crbug.com/694670 clang-cl doesn't currently support %'ing +// __COUNTER__, so eventually it will emit the dword form of push. +// TODO(scottmg): Reinvestigate a short sequence that will work on both +// compilers once clang supports more intrinsics. See https://crbug.com/693713. +#if defined(__clang__) +#define IMMEDIATE_CRASH() \ + ({ \ + {__asm int 3 __asm ud2 __asm push __COUNTER__}; \ + __builtin_unreachable(); \ + }) +#else +#define IMMEDIATE_CRASH() __debugbreak() +#endif // __clang__ + +#else +#error Port +#endif + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG, so the check will be executed regardless of +// compilation mode. +// +// We make sure CHECK et al. always evaluates their arguments, as +// doing CHECK(FunctionWithSideEffect()) is a common idiom. + +#if defined(OFFICIAL_BUILD) && defined(NDEBUG) + +// Make all CHECK functions discard their log strings to reduce code bloat, and +// improve performance, for official release builds. +// +// This is not calling BreakDebugger since this is called frequently, and +// calling an out-of-line function instead of a noreturn inline macro prevents +// compiler optimizations. +#define CHECK(condition) \ + UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_STREAM_PARAMETERS + +// PCHECK includes the system error code, which is useful for determining +// why the condition failed. In official builds, preserve only the error code +// message so that it is available in crash reports. The stringified +// condition and any additional stream parameters are dropped. +#define PCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(FATAL), UNLIKELY(!(condition))); \ + EAT_STREAM_PARAMETERS + +#define CHECK_OP(name, op, val1, val2) CHECK((val1)op(val2)) + +#else // !(OFFICIAL_BUILD && NDEBUG) + +#if defined(_PREFAST_) && defined(OS_WIN) +// Use __analysis_assume to tell the VC++ static analysis engine that +// assert conditions are true, to suppress warnings. The LAZY_STREAM +// parameter doesn't reference 'condition' in /analyze builds because +// this evaluation confuses /analyze. The !! before condition is because +// __analysis_assume gets confused on some conditions: +// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/ + +#define CHECK(condition) \ + __analysis_assume(!!(condition)), LAZY_STREAM(LOG_STREAM(FATAL), false) \ + << "Check failed: " #condition ". " + +#define PCHECK(condition) \ + __analysis_assume(!!(condition)), LAZY_STREAM(PLOG_STREAM(FATAL), false) \ + << "Check failed: " #condition ". " + +#else // _PREFAST_ + +// Do as much work as possible out of line to reduce inline code size. +#define CHECK(condition) \ + LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \ + !ANALYZER_ASSUME_TRUE(condition)) + +#define PCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \ + << "Check failed: " #condition ". " + +#endif // _PREFAST_ + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. +// The 'switch' is used to prevent the 'else' from being ambiguous when the +// macro is used in an 'if' clause such as: +// if (a == 1) +// CHECK_EQ(2, a); +#define CHECK_OP(name, op, val1, val2) \ + switch (0) \ + case 0: \ + default: \ + if (::logging::CheckOpResult true_if_passed = \ + ::logging::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2)) \ + ; \ + else \ + ::logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()) \ + .stream() + +#endif // !(OFFICIAL_BUILD && NDEBUG) + +// This formats a value for a failing CHECK_XX statement. Ordinarily, +// it uses the definition for operator<<, with a few special cases below. +template +inline typename std::enable_if< + base::internal::SupportsOstreamOperator::value && + !std::is_function::type>::value, + void>::type +MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << v; +} + +// Provide an overload for functions and function pointers. Function pointers +// don't implicitly convert to void* but do implicitly convert to bool, so +// without this function pointers are always printed as 1 or 0. (MSVC isn't +// standards-conforming here and converts function pointers to regular +// pointers, so this is a no-op for MSVC.) +template +inline typename std::enable_if< + std::is_function::type>::value, + void>::type +MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << reinterpret_cast(v); +} + +// We need overloads for enums that don't support operator<<. +// (i.e. scoped enums where no operator<< overload was declared). +template +inline typename std::enable_if< + !base::internal::SupportsOstreamOperator::value && + std::is_enum::value, + void>::type +MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << static_cast::type>(v); +} + +// We need an explicit overload for std::nullptr_t. +void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p); + +// Build the error message string. This is separate from the "Impl" +// function template because it is not performance critical and so can +// be out of line, while the "Impl" code should be inline. Caller +// takes ownership of the returned string. +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + std::ostringstream ss; + ss << names << " ("; + MakeCheckOpValueString(&ss, v1); + ss << " vs. "; + MakeCheckOpValueString(&ss, v2); + ss << ")"; + std::string* msg = new std::string(ss.str()); + return msg; +} + +// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated +// in logging.cc. +extern template std::string* MakeCheckOpString(const int&, + const int&, + const char* names); +extern template std::string* MakeCheckOpString( + const unsigned long&, + const unsigned long&, + const char* names); +extern template std::string* MakeCheckOpString( + const unsigned long&, + const unsigned int&, + const char* names); +extern template std::string* MakeCheckOpString( + const unsigned int&, + const unsigned long&, + const char* names); +extern template std::string* MakeCheckOpString( + const std::string&, + const std::string&, + const char* name); + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +// +// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under +// static analysis builds, blocks analysis of the current path if the +// condition is false. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ + return NULL; \ + else \ + return ::logging::MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ + return NULL; \ + else \ + return ::logging::MakeCheckOpString(v1, v2, names); \ + } +DEFINE_CHECK_OP_IMPL(EQ, ==) +DEFINE_CHECK_OP_IMPL(NE, !=) +DEFINE_CHECK_OP_IMPL(LE, <=) +DEFINE_CHECK_OP_IMPL(LT, <) +DEFINE_CHECK_OP_IMPL(GE, >=) +DEFINE_CHECK_OP_IMPL(GT, >) +#undef DEFINE_CHECK_OP_IMPL + +#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(LT, <, val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(GT, >, val1, val2) + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define DCHECK_IS_ON() 0 +#else +#define DCHECK_IS_ON() 1 +#endif + +// Definitions for DLOG et al. + +#if DCHECK_IS_ON() + +#define DLOG_IS_ON(severity) LOG_IS_ON(severity) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) +#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition) + +#else // DCHECK_IS_ON() + +// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition| +// (which may reference a variable defined only if DCHECK_IS_ON()). +// Contrast this with DCHECK et al., which has different behavior. + +#define DLOG_IS_ON(severity) false +#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS +#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS +#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS + +#endif // DCHECK_IS_ON() + +#define DLOG(severity) LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) + +#define DPLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity)) + +// Definitions for DCHECK et al. + +#if DCHECK_IS_ON() + +#if DCHECK_IS_CONFIGURABLE +extern LogSeverity LOG_DCHECK; +#else +const LogSeverity LOG_DCHECK = LOG_FATAL; +#endif + +#else // DCHECK_IS_ON() + +// There may be users of LOG_DCHECK that are enabled independently +// of DCHECK_IS_ON(), so default to FATAL logging for those. +const LogSeverity LOG_DCHECK = LOG_FATAL; + +#endif // DCHECK_IS_ON() + +// DCHECK et al. make sure to reference |condition| regardless of +// whether DCHECKs are enabled; this is so that we don't get unused +// variable warnings if the only use of a variable is in a DCHECK. +// This behavior is different from DLOG_IF et al. +// +// Note that the definition of the DCHECK macros depends on whether or not +// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use +// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries. + +#if defined(_PREFAST_) && defined(OS_WIN) +// See comments on the previous use of __analysis_assume. + +#define DCHECK(condition) \ + __analysis_assume(!!(condition)), LAZY_STREAM(LOG_STREAM(DCHECK), false) \ + << "Check failed: " #condition ". " + +#define DPCHECK(condition) \ + __analysis_assume(!!(condition)), LAZY_STREAM(PLOG_STREAM(DCHECK), false) \ + << "Check failed: " #condition ". " + +#else // !(defined(_PREFAST_) && defined(OS_WIN)) + +#if DCHECK_IS_ON() + +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \ + << "Check failed: " #condition ". " +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \ + << "Check failed: " #condition ". " + +#else // DCHECK_IS_ON() + +#define DCHECK(condition) EAT_STREAM_PARAMETERS << !(condition) +#define DPCHECK(condition) EAT_STREAM_PARAMETERS << !(condition) + +#endif // DCHECK_IS_ON() + +#endif // defined(_PREFAST_) && defined(OS_WIN) + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use DCHECK_EQ et al below. +// The 'switch' is used to prevent the 'else' from being ambiguous when the +// macro is used in an 'if' clause such as: +// if (a == 1) +// DCHECK_EQ(2, a); +#if DCHECK_IS_ON() + +#define DCHECK_OP(name, op, val1, val2) \ + switch (0) \ + case 0: \ + default: \ + if (::logging::CheckOpResult true_if_passed = \ + DCHECK_IS_ON() ? ::logging::Check##name##Impl( \ + (val1), (val2), #val1 " " #op " " #val2) \ + : nullptr) \ + ; \ + else \ + ::logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \ + true_if_passed.message()) \ + .stream() + +#else // DCHECK_IS_ON() + +// When DCHECKs aren't enabled, DCHECK_OP still needs to reference operator<< +// overloads for |val1| and |val2| to avoid potential compiler warnings about +// unused functions. For the same reason, it also compares |val1| and |val2| +// using |op|. +// +// Note that the contract of DCHECK_EQ, etc is that arguments are only evaluated +// once. Even though |val1| and |val2| appear twice in this version of the macro +// expansion, this is OK, since the expression is never actually evaluated. +#define DCHECK_OP(name, op, val1, val2) \ + EAT_STREAM_PARAMETERS << (::logging::MakeCheckOpValueString( \ + ::logging::g_swallow_stream, val1), \ + ::logging::MakeCheckOpValueString( \ + ::logging::g_swallow_stream, val2), \ + (val1)op(val2)) + +#endif // DCHECK_IS_ON() + +// Equality/Inequality checks - compare two values, and log a +// LOG_DCHECK message including the two values when the result is not +// as expected. The values must have operator<<(ostream, ...) +// defined. +// +// You may append to the error message like so: +// DCHECK_NE(1, 2) << "The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// DCHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is NULL. In new code, prefer nullptr instead. To +// work around this for C++98, simply static_cast NULL to the type of the +// desired pointer. + +#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) +#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) +#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) +#define DCHECK_LT(val1, val2) DCHECK_OP(LT, <, val1, val2) +#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) +#define DCHECK_GT(val1, val2) DCHECK_OP(GT, >, val1, val2) + +#if !DCHECK_IS_ON() && defined(OS_CHROMEOS) +// Implement logging of NOTREACHED() as a dedicated function to get function +// call overhead down to a minimum. +void LogErrorNotReached(const char* file, int line); +#define NOTREACHED() \ + true ? ::logging::LogErrorNotReached(__FILE__, __LINE__) \ + : EAT_STREAM_PARAMETERS +#else +#define NOTREACHED() DCHECK(false) +#endif + +// Redefine the standard assert to use our nice log files +#undef assert +#define assert(x) DLOG_ASSERT(x) + +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class LogMessage { + public: + // Used for LOG(severity). + LogMessage(const char* file, int line, LogSeverity severity); + + // Used for CHECK(). Implied severity = LOG_FATAL. + LogMessage(const char* file, int line, const char* condition); + + // Used for CHECK_EQ(), etc. Takes ownership of the given string. + // Implied severity = LOG_FATAL. + LogMessage(const char* file, int line, std::string* result); + + // Used for DCHECK_EQ(), etc. Takes ownership of the given string. + LogMessage(const char* file, + int line, + LogSeverity severity, + std::string* result); + + ~LogMessage(); + + std::ostream& stream() { return stream_; } + + LogSeverity severity() { return severity_; } + std::string str() { return stream_.str(); } + + private: + void Init(const char* file, int line); + + LogSeverity severity_; + std::ostringstream stream_; + size_t message_start_; // Offset of the start of the message (past prefix + // info). + // The file and line information passed in to the constructor. + const char* file_; + const int line_; + +#if defined(OS_WIN) + // Stores the current value of GetLastError in the constructor and restores + // it in the destructor by calling SetLastError. + // This is useful since the LogMessage class uses a lot of Win32 calls + // that will lose the value of GLE and the code that called the log function + // will have lost the thread error value when the log call returns. + class SaveLastError { + public: + SaveLastError(); + ~SaveLastError(); + + unsigned long get_error() const { return last_error_; } + + protected: + unsigned long last_error_; + }; + + SaveLastError last_error_; +#endif + + DISALLOW_COPY_AND_ASSIGN(LogMessage); +}; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() = default; + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) {} +}; + +#if defined(OS_WIN) +typedef unsigned long SystemErrorCode; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +typedef int SystemErrorCode; +#endif + +// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to +// pull in windows.h just for GetLastError() and DWORD. +SystemErrorCode GetLastSystemErrorCode(); +std::string SystemErrorCodeToString(SystemErrorCode error_code); + +#if defined(OS_WIN) +// Appends a formatted system message of the GetLastError() type. +class Win32ErrorLogMessage { + public: + Win32ErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + + // Appends the error message before destructing the encapsulated class. + ~Win32ErrorLogMessage(); + + std::ostream& stream() { return log_message_.stream(); } + + private: + SystemErrorCode err_; + LogMessage log_message_; + + DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage); +}; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +// Appends a formatted system message of the errno type +class ErrnoLogMessage { + public: + ErrnoLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + + // Appends the error message before destructing the encapsulated class. + ~ErrnoLogMessage(); + + std::ostream& stream() { return log_message_.stream(); } + + private: + SystemErrorCode err_; + LogMessage log_message_; + + DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage); +}; +#endif // OS_WIN + +// Closes the log file explicitly if open. +// NOTE: Since the log file is opened as necessary by the action of logging +// statements, there's no guarantee that it will stay closed +// after this call. +void CloseLogFile(); + +// Async signal safe logging mechanism. +void RawLog(int level, const char* message); + +#define RAW_LOG(level, message) \ + ::logging::RawLog(::logging::LOG_##level, message) + +#define RAW_CHECK(condition) \ + do { \ + if (!(condition)) \ + ::logging::RawLog(::logging::LOG_FATAL, \ + "Check failed: " #condition "\n"); \ + } while (0) + +#if defined(OS_WIN) +// Returns true if logging to file is enabled. +bool IsLoggingToFileEnabled(); + +// Returns the default log file path. +std::wstring GetLogFileFullPath(); +#endif + +} // namespace logging + +// Note that "The behavior of a C++ program is undefined if it adds declarations +// or definitions to namespace std or to a namespace within namespace std unless +// otherwise specified." --C++11[namespace.std] +// +// We've checked that this particular definition has the intended behavior on +// our implementations, but it's prone to breaking in the future, and please +// don't imitate this in your own definitions without checking with some +// standard library experts. +namespace std { +// These functions are provided as a convenience for logging, which is where we +// use streams (it is against Google style to use streams in other places). It +// is designed to allow you to emit non-ASCII Unicode strings to the log file, +// which is normally ASCII. It is relatively slow, so try not to use it for +// common cases. Non-ASCII characters will be converted to UTF-8 by these +// operators. +std::ostream& operator<<(std::ostream& out, const wchar_t* wstr); +inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) { + return out << wstr.c_str(); +} +} // namespace std + +// The NOTIMPLEMENTED() macro annotates codepaths which have not been +// implemented yet. If output spam is a serious concern, +// NOTIMPLEMENTED_LOG_ONCE can be used. + +#if defined(COMPILER_GCC) +// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name +// of the current function in the NOTIMPLEMENTED message. +#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__ +#else +#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED" +#endif + +#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD) +#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS +#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS +#else +#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG +#define NOTIMPLEMENTED_LOG_ONCE() \ + do { \ + static bool logged_once = false; \ + LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \ + logged_once = true; \ + } while (0); \ + EAT_STREAM_PARAMETERS +#endif + +#endif // BASE_LOGGING_H_ diff --git a/src/3rdparty/gn/base/mac/bundle_locations.h b/src/3rdparty/gn/base/mac/bundle_locations.h new file mode 100644 index 00000000000..2bda76e6411 --- /dev/null +++ b/src/3rdparty/gn/base/mac/bundle_locations.h @@ -0,0 +1,65 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_BUNDLE_LOCATIONS_H_ +#define BASE_MAC_BUNDLE_LOCATIONS_H_ + +#include "base/files/file_path.h" + +#if defined(__OBJC__) +#import +#else // __OBJC__ +class NSBundle; +#endif // __OBJC__ + +namespace base { + +class FilePath; + +namespace mac { + +// This file provides several functions to explicitly request the various +// component bundles of Chrome. Please use these methods rather than calling +// +[NSBundle mainBundle] or CFBundleGetMainBundle(). +// +// Terminology +// - "Outer Bundle" - This is the main bundle for Chrome; it's what +// +[NSBundle mainBundle] returns when Chrome is launched normally. +// +// - "Main Bundle" - This is the bundle from which Chrome was launched. +// This will be the same as the outer bundle except when Chrome is launched +// via an app shortcut, in which case this will return the app shortcut's +// bundle rather than the main Chrome bundle. +// +// - "Framework Bundle" - This is the bundle corresponding to the Chrome +// framework. +// +// Guidelines for use: +// - To access a resource, the Framework bundle should be used. +// - If the choice is between the Outer or Main bundles then please choose +// carefully. Most often the Outer bundle will be the right choice, but for +// cases such as adding an app to the "launch on startup" list, the Main +// bundle is probably the one to use. + +// Methods for retrieving the various bundles. +NSBundle* MainBundle(); +FilePath MainBundlePath(); +NSBundle* OuterBundle(); +FilePath OuterBundlePath(); +NSBundle* FrameworkBundle(); +FilePath FrameworkBundlePath(); + +// Set the bundle that the preceding functions will return, overriding the +// default values. Restore the default by passing in |nil|. +void SetOverrideOuterBundle(NSBundle* bundle); +void SetOverrideFrameworkBundle(NSBundle* bundle); + +// Same as above but accepting a FilePath argument. +void SetOverrideOuterBundlePath(const FilePath& file_path); +void SetOverrideFrameworkBundlePath(const FilePath& file_path); + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_BUNDLE_LOCATIONS_H_ diff --git a/src/3rdparty/gn/base/mac/mac_logging.h b/src/3rdparty/gn/base/mac/mac_logging.h new file mode 100644 index 00000000000..5ef75f38323 --- /dev/null +++ b/src/3rdparty/gn/base/mac/mac_logging.h @@ -0,0 +1,74 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_MAC_LOGGING_H_ +#define BASE_MAC_MAC_LOGGING_H_ + +#include "base/logging.h" +#include "base/macros.h" +#include "util/build_config.h" + +#if defined(OS_IOS) +#include +#else +#include +#endif + +// Use the OSSTATUS_LOG family to log messages related to errors in Mac OS X +// system routines that report status via an OSStatus or OSErr value. It is +// similar to the PLOG family which operates on errno, but because there is no +// global (or thread-local) OSStatus or OSErr value, the specific error must +// be supplied as an argument to the OSSTATUS_LOG macro. The message logged +// will contain the symbolic constant name corresponding to the status value, +// along with the value itself. +// +// OSErr is just an older 16-bit form of the newer 32-bit OSStatus. Despite +// the name, OSSTATUS_LOG can be used equally well for OSStatus and OSErr. + +namespace logging { + +// Returns a UTF8 description from an OS X Status error. +std::string DescriptionFromOSStatus(OSStatus err); + +class OSStatusLogMessage : public logging::LogMessage { + public: + OSStatusLogMessage(const char* file_path, + int line, + LogSeverity severity, + OSStatus status); + ~OSStatusLogMessage(); + + private: + OSStatus status_; + + DISALLOW_COPY_AND_ASSIGN(OSStatusLogMessage); +}; + +} // namespace logging + +#define OSSTATUS_LOG_STREAM(severity, status) \ + COMPACT_GOOGLE_LOG_EX_##severity(OSStatusLogMessage, status).stream() + +#define OSSTATUS_LOG(severity, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), LOG_IS_ON(severity)) +#define OSSTATUS_LOG_IF(severity, condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \ + LOG_IS_ON(severity) && (condition)) + +#define OSSTATUS_CHECK(condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), !(condition)) \ + << "Check failed: " #condition << ". " + +#define OSSTATUS_DLOG(severity, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), DLOG_IS_ON(severity)) +#define OSSTATUS_DLOG_IF(severity, condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \ + DLOG_IS_ON(severity) && (condition)) + +#define OSSTATUS_DCHECK(condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \ + DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition << ". " + +#endif // BASE_MAC_MAC_LOGGING_H_ diff --git a/src/3rdparty/gn/base/mac/mac_logging.mm b/src/3rdparty/gn/base/mac/mac_logging.mm new file mode 100644 index 00000000000..f7c30528bc5 --- /dev/null +++ b/src/3rdparty/gn/base/mac/mac_logging.mm @@ -0,0 +1,42 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/mac_logging.h" + +#import + +#include + +#include "util/build_config.h" + +#if !defined(OS_IOS) +#include +#endif + +namespace logging { + +std::string DescriptionFromOSStatus(OSStatus err) { + NSError* error = + [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; + return error.description.UTF8String; +} + +OSStatusLogMessage::OSStatusLogMessage(const char* file_path, + int line, + LogSeverity severity, + OSStatus status) + : LogMessage(file_path, line, severity), status_(status) {} + +OSStatusLogMessage::~OSStatusLogMessage() { +#if defined(OS_IOS) + // TODO(crbug.com/546375): Consider using NSError with NSOSStatusErrorDomain + // to try to get a description of the failure. + stream() << ": " << status_; +#else + stream() << ": " << DescriptionFromOSStatus(status_) << " (" << status_ + << ")"; +#endif +} + +} // namespace logging diff --git a/src/3rdparty/gn/base/mac/scoped_cftyperef.h b/src/3rdparty/gn/base/mac/scoped_cftyperef.h new file mode 100644 index 00000000000..a602fd9cbb2 --- /dev/null +++ b/src/3rdparty/gn/base/mac/scoped_cftyperef.h @@ -0,0 +1,48 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_SCOPED_CFTYPEREF_H_ +#define BASE_MAC_SCOPED_CFTYPEREF_H_ + +#include + +#include "base/mac/scoped_typeref.h" + +namespace base { + +// ScopedCFTypeRef<> is patterned after std::unique_ptr<>, but maintains +// ownership of a CoreFoundation object: any object that can be represented +// as a CFTypeRef. Style deviations here are solely for compatibility with +// std::unique_ptr<>'s interface, with which everyone is already familiar. +// +// By default, ScopedCFTypeRef<> takes ownership of an object (in the +// constructor or in reset()) by taking over the caller's existing ownership +// claim. The caller must own the object it gives to ScopedCFTypeRef<>, and +// relinquishes an ownership claim to that object. ScopedCFTypeRef<> does not +// call CFRetain(). This behavior is parameterized by the |OwnershipPolicy| +// enum. If the value |RETAIN| is passed (in the constructor or in reset()), +// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial +// ownership is not changed. + +namespace internal { + +template +struct ScopedCFTypeRefTraits { + static CFT InvalidValue() { return nullptr; } + static CFT Retain(CFT object) { + CFRetain(object); + return object; + } + static void Release(CFT object) { CFRelease(object); } +}; + +} // namespace internal + +template +using ScopedCFTypeRef = + ScopedTypeRef>; + +} // namespace base + +#endif // BASE_MAC_SCOPED_CFTYPEREF_H_ diff --git a/src/3rdparty/gn/base/mac/scoped_typeref.h b/src/3rdparty/gn/base/mac/scoped_typeref.h new file mode 100644 index 00000000000..659ee3426b0 --- /dev/null +++ b/src/3rdparty/gn/base/mac/scoped_typeref.h @@ -0,0 +1,138 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_SCOPED_TYPEREF_H_ +#define BASE_MAC_SCOPED_TYPEREF_H_ + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/scoped_policy.h" + +namespace base { + +// ScopedTypeRef<> is patterned after std::unique_ptr<>, but maintains ownership +// of a reference to any type that is maintained by Retain and Release methods. +// +// The Traits structure must provide the Retain and Release methods for type T. +// A default ScopedTypeRefTraits is used but not defined, and should be defined +// for each type to use this interface. For example, an appropriate definition +// of ScopedTypeRefTraits for CGLContextObj would be: +// +// template<> +// struct ScopedTypeRefTraits { +// static CGLContextObj InvalidValue() { return nullptr; } +// static CGLContextObj Retain(CGLContextObj object) { +// CGLContextRetain(object); +// return object; +// } +// static void Release(CGLContextObj object) { CGLContextRelease(object); } +// }; +// +// For the many types that have pass-by-pointer create functions, the function +// InitializeInto() is provided to allow direct initialization and assumption +// of ownership of the object. For example, continuing to use the above +// CGLContextObj specialization: +// +// base::ScopedTypeRef context; +// CGLCreateContext(pixel_format, share_group, context.InitializeInto()); +// +// For initialization with an existing object, the caller may specify whether +// the ScopedTypeRef<> being initialized is assuming the caller's existing +// ownership of the object (and should not call Retain in initialization) or if +// it should not assume this ownership and must create its own (by calling +// Retain in initialization). This behavior is based on the |policy| parameter, +// with |ASSUME| for the former and |RETAIN| for the latter. The default policy +// is to |ASSUME|. + +template +struct ScopedTypeRefTraits; + +template > +class ScopedTypeRef { + public: + typedef T element_type; + + explicit constexpr ScopedTypeRef( + __unsafe_unretained T object = Traits::InvalidValue(), + base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) + : object_(object) { + if (object_ && policy == base::scoped_policy::RETAIN) + object_ = Traits::Retain(object_); + } + + ScopedTypeRef(const ScopedTypeRef& that) : object_(that.object_) { + if (object_) + object_ = Traits::Retain(object_); + } + + // This allows passing an object to a function that takes its superclass. + template + explicit ScopedTypeRef(const ScopedTypeRef& that_as_subclass) + : object_(that_as_subclass.get()) { + if (object_) + object_ = Traits::Retain(object_); + } + + ScopedTypeRef(ScopedTypeRef&& that) : object_(that.object_) { + that.object_ = Traits::InvalidValue(); + } + + ~ScopedTypeRef() { + if (object_) + Traits::Release(object_); + } + + ScopedTypeRef& operator=(const ScopedTypeRef& that) { + reset(that.get(), base::scoped_policy::RETAIN); + return *this; + } + + // This is to be used only to take ownership of objects that are created + // by pass-by-pointer create functions. To enforce this, require that the + // object be reset to NULL before this may be used. + T* InitializeInto() WARN_UNUSED_RESULT { + DCHECK(!object_); + return &object_; + } + + void reset(__unsafe_unretained T object = Traits::InvalidValue(), + base::scoped_policy::OwnershipPolicy policy = + base::scoped_policy::ASSUME) { + if (object && policy == base::scoped_policy::RETAIN) + object = Traits::Retain(object); + if (object_) + Traits::Release(object_); + object_ = object; + } + + bool operator==(__unsafe_unretained T that) const { return object_ == that; } + + bool operator!=(__unsafe_unretained T that) const { return object_ != that; } + + operator T() const __attribute((ns_returns_not_retained)) { return object_; } + + T get() const __attribute((ns_returns_not_retained)) { return object_; } + + void swap(ScopedTypeRef& that) { + __unsafe_unretained T temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // ScopedTypeRef<>::release() is like std::unique_ptr<>::release. It is NOT + // a wrapper for Release(). To force a ScopedTypeRef<> object to call + // Release(), use ScopedTypeRef<>::reset(). + T release() __attribute((ns_returns_not_retained)) WARN_UNUSED_RESULT { + __unsafe_unretained T temp = object_; + object_ = Traits::InvalidValue(); + return temp; + } + + private: + __unsafe_unretained T object_; +}; + +} // namespace base + +#endif // BASE_MAC_SCOPED_TYPEREF_H_ diff --git a/src/3rdparty/gn/base/macros.h b/src/3rdparty/gn/base/macros.h new file mode 100644 index 00000000000..321f65bc513 --- /dev/null +++ b/src/3rdparty/gn/base/macros.h @@ -0,0 +1,94 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains macros and macro-like constructs (e.g., templates) that +// are commonly used throughout Chromium source. (It may also contain things +// that are closely related to things that are commonly used that belong in this +// file.) + +#ifndef BASE_MACROS_H_ +#define BASE_MACROS_H_ + +#include // For size_t. + +// Distinguish mips32. +#if defined(__mips__) && (_MIPS_SIM == _ABIO32) && !defined(__mips32__) +#define __mips32__ +#endif + +// Distinguish mips64. +#if defined(__mips__) && (_MIPS_SIM == _ABI64) && !defined(__mips64__) +#define __mips64__ +#endif + +// Put this in the declarations for a class to be uncopyable. +#define DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete + +// Put this in the declarations for a class to be unassignable. +#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete + +// Put this in the declarations for a class to be uncopyable and unassignable. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + DISALLOW_COPY(TypeName); \ + DISALLOW_ASSIGN(TypeName) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// This is especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// The arraysize(arr) macro returns the # of elements in an array arr. The +// expression is a compile-time constant, and therefore can be used in defining +// new arrays, for example. If you use arraysize on a pointer by mistake, you +// will get a compile-time error. For the technical details, refer to +// http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +// +// DEPRECATED, please use base::size(array) instead. +// TODO(https://crbug.com/837308): Replace existing arraysize usages. +template +char (&ArraySizeHelper(T (&array)[N]))[N]; +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +// Used to explicitly mark the return value of a function as unused. If you are +// really sure you don't want to do anything with the return value of a function +// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: +// +// std::unique_ptr my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// ignore_result(my_var.release()); +// +template +inline void ignore_result(const T&) {} + +namespace base { + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. This is +// thread-safe. +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DEPRECATED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Please don't use this macro. Use a function-local static of type +// base::NoDestructor instead: +// +// Factory& Factory::GetInstance() { +// static base::NoDestructor instance; +// return *instance; +// } +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments + +// Workaround for MSVC, which expands __VA_ARGS__ as one macro argument. To +// work around this bug, wrap the entire expression in this macro... +#define CR_EXPAND_ARG(arg) arg + +} // namespace base + +#endif // BASE_MACROS_H_ diff --git a/src/3rdparty/gn/base/md5.cc b/src/3rdparty/gn/base/md5.cc new file mode 100644 index 00000000000..c66f7b220c1 --- /dev/null +++ b/src/3rdparty/gn/base/md5.cc @@ -0,0 +1,299 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The original file was copied from sqlite, and was in the public domain. + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "base/md5.h" + +#include + +namespace { + +struct Context { + uint32_t buf[4]; + uint32_t bits[2]; + uint8_t in[64]; +}; + +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(uint8_t* buf, unsigned longs) { + do { + uint32_t temp = + static_cast(static_cast(buf[3]) << 8 | buf[2]) + << 16 | + (static_cast(buf[1]) << 8 | buf[0]); + *reinterpret_cast(buf) = temp; + buf += 4; + } while (--longs); +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32_t buf[4], const uint32_t in[16]) { + uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +} // namespace + +namespace base { + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(MD5Context* context) { + struct Context* ctx = reinterpret_cast(context); + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(MD5Context* context, const StringPiece& data) { + struct Context* ctx = reinterpret_cast(context); + const uint8_t* buf = reinterpret_cast(data.data()); + size_t len = data.size(); + + /* Update bitcount */ + + uint32_t t = ctx->bits[0]; + if ((ctx->bits[0] = t + (static_cast(len) << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += static_cast(len >> 29); + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + uint8_t* p = static_cast(ctx->in + t); + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(MD5Digest* digest, MD5Context* context) { + struct Context* ctx = reinterpret_cast(context); + unsigned count; + uint8_t* p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + memcpy(&ctx->in[14 * sizeof(ctx->bits[0])], &ctx->bits[0], + sizeof(ctx->bits[0])); + memcpy(&ctx->in[15 * sizeof(ctx->bits[1])], &ctx->bits[1], + sizeof(ctx->bits[1])); + + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + byteReverse(reinterpret_cast(ctx->buf), 4); + memcpy(digest->a, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context) { + /* MD5Final mutates the MD5Context*. Make a copy for generating the + intermediate value. */ + MD5Context context_copy; + memcpy(&context_copy, context, sizeof(context_copy)); + MD5Final(digest, &context_copy); +} + +std::string MD5DigestToBase16(const MD5Digest& digest) { + static char const zEncode[] = "0123456789abcdef"; + + std::string ret; + ret.resize(32); + + for (int i = 0, j = 0; i < 16; i++, j += 2) { + uint8_t a = digest.a[i]; + ret[j] = zEncode[(a >> 4) & 0xf]; + ret[j + 1] = zEncode[a & 0xf]; + } + return ret; +} + +void MD5Sum(const void* data, size_t length, MD5Digest* digest) { + MD5Context ctx; + MD5Init(&ctx); + MD5Update(&ctx, StringPiece(reinterpret_cast(data), length)); + MD5Final(digest, &ctx); +} + +std::string MD5String(const StringPiece& str) { + MD5Digest digest; + MD5Sum(str.data(), str.length(), &digest); + return MD5DigestToBase16(digest); +} + +} // namespace base diff --git a/src/3rdparty/gn/base/md5.h b/src/3rdparty/gn/base/md5.h new file mode 100644 index 00000000000..16a0a6f90d9 --- /dev/null +++ b/src/3rdparty/gn/base/md5.h @@ -0,0 +1,76 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MD5_H_ +#define BASE_MD5_H_ + +#include +#include + +#include "base/strings/string_piece.h" + +namespace base { + +// MD5 stands for Message Digest algorithm 5. +// MD5 is a robust hash function, designed for cyptography, but often used +// for file checksums. The code is complex and slow, but has few +// collisions. +// See Also: +// http://en.wikipedia.org/wiki/MD5 + +// These functions perform MD5 operations. The simplest call is MD5Sum() to +// generate the MD5 sum of the given data. +// +// You can also compute the MD5 sum of data incrementally by making multiple +// calls to MD5Update(): +// MD5Context ctx; // intermediate MD5 data: do not use +// MD5Init(&ctx); +// MD5Update(&ctx, data1, length1); +// MD5Update(&ctx, data2, length2); +// ... +// +// MD5Digest digest; // the result of the computation +// MD5Final(&digest, &ctx); +// +// You can call MD5DigestToBase16() to generate a string of the digest. + +// The output of an MD5 operation. +struct MD5Digest { + uint8_t a[16]; +}; + +// Used for storing intermediate data during an MD5 computation. Callers +// should not access the data. +typedef char MD5Context[88]; + +// Initializes the given MD5 context structure for subsequent calls to +// MD5Update(). +void MD5Init(MD5Context* context); + +// For the given buffer of |data| as a StringPiece, updates the given MD5 +// context with the sum of the data. You can call this any number of times +// during the computation, except that MD5Init() must have been called first. +void MD5Update(MD5Context* context, const StringPiece& data); + +// Finalizes the MD5 operation and fills the buffer with the digest. +void MD5Final(MD5Digest* digest, MD5Context* context); + +// MD5IntermediateFinal() generates a digest without finalizing the MD5 +// operation. Can be used to generate digests for the input seen thus far, +// without affecting the digest generated for the entire input. +void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context); + +// Converts a digest into human-readable hexadecimal. +std::string MD5DigestToBase16(const MD5Digest& digest); + +// Computes the MD5 sum of the given data buffer with the given length. +// The given 'digest' structure will be filled with the result data. +void MD5Sum(const void* data, size_t length, MD5Digest* digest); + +// Returns the MD5 (in hexadecimal) of a string. +std::string MD5String(const StringPiece& str); + +} // namespace base + +#endif // BASE_MD5_H_ diff --git a/src/3rdparty/gn/base/memory/free_deleter.h b/src/3rdparty/gn/base/memory/free_deleter.h new file mode 100644 index 00000000000..e12795c7a88 --- /dev/null +++ b/src/3rdparty/gn/base/memory/free_deleter.h @@ -0,0 +1,23 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_FREE_DELETER_H_ +#define BASE_MEMORY_FREE_DELETER_H_ + +#include + +namespace base { + +// Function object which invokes 'free' on its parameter, which must be +// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr: +// +// std::unique_ptr foo_ptr( +// static_cast(malloc(sizeof(int)))); +struct FreeDeleter { + inline void operator()(void* ptr) const { free(ptr); } +}; + +} // namespace base + +#endif // BASE_MEMORY_FREE_DELETER_H_ diff --git a/src/3rdparty/gn/base/memory/ptr_util.h b/src/3rdparty/gn/base/memory/ptr_util.h new file mode 100644 index 00000000000..42f4f49eebd --- /dev/null +++ b/src/3rdparty/gn/base/memory/ptr_util.h @@ -0,0 +1,23 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_PTR_UTIL_H_ +#define BASE_MEMORY_PTR_UTIL_H_ + +#include +#include + +namespace base { + +// Helper to transfer ownership of a raw pointer to a std::unique_ptr. +// Note that std::unique_ptr has very different semantics from +// std::unique_ptr: do not use this helper for array allocations. +template +std::unique_ptr WrapUnique(T* ptr) { + return std::unique_ptr(ptr); +} + +} // namespace base + +#endif // BASE_MEMORY_PTR_UTIL_H_ diff --git a/src/3rdparty/gn/base/memory/raw_scoped_refptr_mismatch_checker.h b/src/3rdparty/gn/base/memory/raw_scoped_refptr_mismatch_checker.h new file mode 100644 index 00000000000..ab8b2abcbb9 --- /dev/null +++ b/src/3rdparty/gn/base/memory/raw_scoped_refptr_mismatch_checker.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ +#define BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ + +#include + +#include "base/template_util.h" + +// It is dangerous to post a task with a T* argument where T is a subtype of +// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the +// object may already have been deleted since it was not held with a +// scoped_refptr. Example: http://crbug.com/27191 +// The following set of traits are designed to generate a compile error +// whenever this antipattern is attempted. + +namespace base { + +// This is a base internal implementation file used by task.h and callback.h. +// Not for public consumption, so we wrap it in namespace internal. +namespace internal { + +template +struct IsRefCountedType : std::false_type {}; + +template +struct IsRefCountedType()->AddRef()), + decltype(std::declval()->Release())>> + : std::true_type {}; + +template +struct NeedsScopedRefptrButGetsRawPtr { + static_assert(!std::is_reference::value, + "NeedsScopedRefptrButGetsRawPtr requires non-reference type."); + + enum { + // Human readable translation: you needed to be a scoped_refptr if you are a + // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase) + // type. + value = std::is_pointer::value && + IsRefCountedType>::value + }; +}; + +} // namespace internal + +} // namespace base + +#endif // BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ diff --git a/src/3rdparty/gn/base/memory/ref_counted.cc b/src/3rdparty/gn/base/memory/ref_counted.cc new file mode 100644 index 00000000000..79ab8535a5e --- /dev/null +++ b/src/3rdparty/gn/base/memory/ref_counted.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/ref_counted.h" + +namespace base { + +namespace subtle { + +bool RefCountedThreadSafeBase::HasOneRef() const { + return ref_count_.IsOne(); +} + +#if defined(ARCH_CPU_64_BIT) +void RefCountedBase::AddRefImpl() const { + // Check if |ref_count_| overflow only on 64 bit archs since the number of + // objects may exceed 2^32. + // To avoid the binary size bloat, use non-inline function here. + CHECK(++ref_count_ > 0); +} +#endif + +#if !defined(ARCH_CPU_X86_FAMILY) +bool RefCountedThreadSafeBase::Release() const { + return ReleaseImpl(); +} +void RefCountedThreadSafeBase::AddRef() const { + AddRefImpl(); +} +#endif + +} // namespace subtle + +} // namespace base diff --git a/src/3rdparty/gn/base/memory/ref_counted.h b/src/3rdparty/gn/base/memory/ref_counted.h new file mode 100644 index 00000000000..8ebdcd68b5b --- /dev/null +++ b/src/3rdparty/gn/base/memory/ref_counted.h @@ -0,0 +1,317 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_REF_COUNTED_H_ +#define BASE_MEMORY_REF_COUNTED_H_ + +#include + +#include + +#include "base/atomic_ref_count.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "util/build_config.h" + +namespace base { +namespace subtle { + +class RefCountedBase { + public: + bool HasOneRef() const { return ref_count_ == 1; } + + protected: + explicit RefCountedBase(StartRefCountFromZeroTag) {} + + explicit RefCountedBase(StartRefCountFromOneTag) : ref_count_(1) {} + + ~RefCountedBase() {} + + void AddRef() const { AddRefImpl(); } + + // Returns true if the object should self-delete. + bool Release() const { + --ref_count_; + + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" + // without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); + + return ref_count_ == 0; + } + + // Returns true if it is safe to read or write the object, from a thread + // safety standpoint. Should be DCHECK'd from the methods of RefCounted + // classes if there is a danger of objects being shared across threads. + // + // This produces fewer false positives than adding a separate SequenceChecker + // into the subclass, because it automatically detaches from the sequence when + // the reference count is 1 (and never fails if there is only one reference). + // + // This means unlike a separate SequenceChecker, it will permit a singly + // referenced object to be passed between threads (not holding a reference on + // the sending thread), but will trap if the sending thread holds onto a + // reference, or if the object is accessed from multiple threads + // simultaneously. + bool IsOnValidSequence() const { return true; } + + private: + template + friend scoped_refptr base::AdoptRef(U*); + + void Adopted() const {} + +#if defined(ARCH_CPU_64_BIT) + void AddRefImpl() const; +#else + void AddRefImpl() const { ++ref_count_; } +#endif + + mutable uint32_t ref_count_ = 0; + + DISALLOW_COPY_AND_ASSIGN(RefCountedBase); +}; + +class RefCountedThreadSafeBase { + public: + bool HasOneRef() const; + + protected: + explicit constexpr RefCountedThreadSafeBase(StartRefCountFromZeroTag) {} + explicit constexpr RefCountedThreadSafeBase(StartRefCountFromOneTag) + : ref_count_(1) {} + + ~RefCountedThreadSafeBase() = default; + +// Release and AddRef are suitable for inlining on X86 because they generate +// very small code sequences. On other platforms (ARM), it causes a size +// regression and is probably not worth it. +#if defined(ARCH_CPU_X86_FAMILY) + // Returns true if the object should self-delete. + bool Release() const { return ReleaseImpl(); } + void AddRef() const { AddRefImpl(); } +#else + // Returns true if the object should self-delete. + bool Release() const; + void AddRef() const; +#endif + + private: + template + friend scoped_refptr base::AdoptRef(U*); + + void Adopted() const {} + + ALWAYS_INLINE void AddRefImpl() const { ref_count_.Increment(); } + + ALWAYS_INLINE bool ReleaseImpl() const { + if (!ref_count_.Decrement()) { + return true; + } + return false; + } + + mutable AtomicRefCount ref_count_{0}; + + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); +}; + +} // namespace subtle + +// ScopedAllowCrossThreadRefCountAccess disables the check documented on +// RefCounted below for rare pre-existing use cases where thread-safety was +// guaranteed through other means (e.g. explicit sequencing of calls across +// execution sequences when bouncing between threads in order). New callers +// should refrain from using this (callsites handling thread-safety through +// locks should use RefCountedThreadSafe per the overhead of its atomics being +// negligible compared to locks anyways and callsites doing explicit sequencing +// should properly std::move() the ref to avoid hitting this check). +// TODO(tzik): Cleanup existing use cases and remove +// ScopedAllowCrossThreadRefCountAccess. +class ScopedAllowCrossThreadRefCountAccess final { + public: + ScopedAllowCrossThreadRefCountAccess() {} + ~ScopedAllowCrossThreadRefCountAccess() {} +}; + +// +// A base class for reference counted classes. Otherwise, known as a cheap +// knock-off of WebKit's RefCounted class. To use this, just extend your +// class from it like so: +// +// class MyFoo : public base::RefCounted { +// ... +// private: +// friend class base::RefCounted; +// ~MyFoo(); +// }; +// +// You should always make your destructor non-public, to avoid any code deleting +// the object accidently while there are references to it. +// +// +// The ref count manipulation to RefCounted is NOT thread safe and has DCHECKs +// to trap unsafe cross thread usage. A subclass instance of RefCounted can be +// passed to another execution sequence only when its ref count is 1. If the ref +// count is more than 1, the RefCounted class verifies the ref updates are made +// on the same execution sequence as the previous ones. The subclass can also +// manually call IsOnValidSequence to trap other non-thread-safe accesses; see +// the documentation for that method. +// +// +// The reference count starts from zero by default, and we intended to migrate +// to start-from-one ref count. Put REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() to +// the ref counted class to opt-in. +// +// If an object has start-from-one ref count, the first scoped_refptr need to be +// created by base::AdoptRef() or base::MakeRefCounted(). We can use +// base::MakeRefCounted() to create create both type of ref counted object. +// +// The motivations to use start-from-one ref count are: +// - Start-from-one ref count doesn't need the ref count increment for the +// first reference. +// - It can detect an invalid object acquisition for a being-deleted object +// that has zero ref count. That tends to happen on custom deleter that +// delays the deletion. +// TODO(tzik): Implement invalid acquisition detection. +// - Behavior parity to Blink's WTF::RefCounted, whose count starts from one. +// And start-from-one ref count is a step to merge WTF::RefCounted into +// base::RefCounted. +// +#define REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() \ + static constexpr ::base::subtle::StartRefCountFromOneTag \ + kRefCountPreference = ::base::subtle::kStartRefCountFromOneTag + +template +class RefCounted; + +template +struct DefaultRefCountedTraits { + static void Destruct(const T* x) { + RefCounted::DeleteInternal(x); + } +}; + +template > +class RefCounted : public subtle::RefCountedBase { + public: + static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = + subtle::kStartRefCountFromZeroTag; + + RefCounted() : subtle::RefCountedBase(T::kRefCountPreference) {} + + void AddRef() const { subtle::RefCountedBase::AddRef(); } + + void Release() const { + if (subtle::RefCountedBase::Release()) { + // Prune the code paths which the static analyzer may take to simulate + // object destruction. Use-after-free errors aren't possible given the + // lifetime guarantees of the refcounting system. + ANALYZER_SKIP_THIS_PATH(); + + Traits::Destruct(static_cast(this)); + } + } + + protected: + ~RefCounted() = default; + + private: + friend struct DefaultRefCountedTraits; + template + static void DeleteInternal(const U* x) { + delete x; + } + + DISALLOW_COPY_AND_ASSIGN(RefCounted); +}; + +// Forward declaration. +template +class RefCountedThreadSafe; + +// Default traits for RefCountedThreadSafe. Deletes the object when its ref +// count reaches 0. Overload to delete it on a different thread etc. +template +struct DefaultRefCountedThreadSafeTraits { + static void Destruct(const T* x) { + // Delete through RefCountedThreadSafe to make child classes only need to be + // friend with RefCountedThreadSafe instead of this struct, which is an + // implementation detail. + RefCountedThreadSafe::DeleteInternal( + x); + } +}; + +// +// A thread-safe variant of RefCounted +// +// class MyFoo : public base::RefCountedThreadSafe { +// ... +// }; +// +// If you're using the default trait, then you should add compile time +// asserts that no one else is deleting your object. i.e. +// private: +// friend class base::RefCountedThreadSafe; +// ~MyFoo(); +// +// We can use REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() with RefCountedThreadSafe +// too. See the comment above the RefCounted definition for details. +template > +class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { + public: + static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = + subtle::kStartRefCountFromZeroTag; + + explicit RefCountedThreadSafe() + : subtle::RefCountedThreadSafeBase(T::kRefCountPreference) {} + + void AddRef() const { subtle::RefCountedThreadSafeBase::AddRef(); } + + void Release() const { + if (subtle::RefCountedThreadSafeBase::Release()) { + ANALYZER_SKIP_THIS_PATH(); + Traits::Destruct(static_cast(this)); + } + } + + protected: + ~RefCountedThreadSafe() = default; + + private: + friend struct DefaultRefCountedThreadSafeTraits; + template + static void DeleteInternal(const U* x) { + delete x; + } + + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe); +}; + +// +// A thread-safe wrapper for some piece of data so we can place other +// things in scoped_refptrs<>. +// +template +class RefCountedData + : public base::RefCountedThreadSafe> { + public: + RefCountedData() : data() {} + RefCountedData(const T& in_value) : data(in_value) {} + RefCountedData(T&& in_value) : data(std::move(in_value)) {} + + T data; + + private: + friend class base::RefCountedThreadSafe>; + ~RefCountedData() = default; +}; + +} // namespace base + +#endif // BASE_MEMORY_REF_COUNTED_H_ diff --git a/src/3rdparty/gn/base/memory/scoped_policy.h b/src/3rdparty/gn/base/memory/scoped_policy.h new file mode 100644 index 00000000000..5dbf2048d64 --- /dev/null +++ b/src/3rdparty/gn/base/memory/scoped_policy.h @@ -0,0 +1,25 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_SCOPED_POLICY_H_ +#define BASE_MEMORY_SCOPED_POLICY_H_ + +namespace base { +namespace scoped_policy { + +// Defines the ownership policy for a scoped object. +enum OwnershipPolicy { + // The scoped object takes ownership of an object by taking over an existing + // ownership claim. + ASSUME, + + // The scoped object will retain the the object and any initial ownership is + // not changed. + RETAIN +}; + +} // namespace scoped_policy +} // namespace base + +#endif // BASE_MEMORY_SCOPED_POLICY_H_ diff --git a/src/3rdparty/gn/base/memory/scoped_refptr.h b/src/3rdparty/gn/base/memory/scoped_refptr.h new file mode 100644 index 00000000000..a2576170bf9 --- /dev/null +++ b/src/3rdparty/gn/base/memory/scoped_refptr.h @@ -0,0 +1,333 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_SCOPED_REFPTR_H_ +#define BASE_MEMORY_SCOPED_REFPTR_H_ + +#include + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/macros.h" + +template +class scoped_refptr; + +namespace base { + +template +class RefCounted; +template +class RefCountedThreadSafe; + +template +scoped_refptr AdoptRef(T* t); + +namespace subtle { + +enum AdoptRefTag { kAdoptRefTag }; +enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag }; +enum StartRefCountFromOneTag { kStartRefCountFromOneTag }; + +template +constexpr bool IsRefCountPreferenceOverridden(const T*, + const RefCounted*) { + return !std::is_same, + std::decay_t>::value; +} + +template +constexpr bool IsRefCountPreferenceOverridden( + const T*, + const RefCountedThreadSafe*) { + return !std::is_same, + std::decay_t>::value; +} + +constexpr bool IsRefCountPreferenceOverridden(...) { + return false; +} + +} // namespace subtle + +// Creates a scoped_refptr from a raw pointer without incrementing the reference +// count. Use this only for a newly created object whose reference count starts +// from 1 instead of 0. +template +scoped_refptr AdoptRef(T* obj) { + using Tag = std::decay_t; + static_assert(std::is_same::value, + "Use AdoptRef only for the reference count starts from one."); + + DCHECK(obj); + DCHECK(obj->HasOneRef()); + obj->Adopted(); + return scoped_refptr(obj, subtle::kAdoptRefTag); +} + +namespace subtle { + +template +scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromZeroTag) { + return scoped_refptr(obj); +} + +template +scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromOneTag) { + return AdoptRef(obj); +} + +} // namespace subtle + +// Constructs an instance of T, which is a ref counted type, and wraps the +// object into a scoped_refptr. +template +scoped_refptr MakeRefCounted(Args&&... args) { + T* obj = new T(std::forward(args)...); + return subtle::AdoptRefIfNeeded(obj, T::kRefCountPreference); +} + +// Takes an instance of T, which is a ref counted type, and wraps the object +// into a scoped_refptr. +template +scoped_refptr WrapRefCounted(T* t) { + return scoped_refptr(t); +} + +} // namespace base + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// private: +// friend class RefCounted; // Allow destruction by RefCounted<>. +// ~MyFoo(); // Destructor must be private/protected. +// }; +// +// void some_function() { +// scoped_refptr foo = MakeRefCounted(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = MakeRefCounted(); +// ... +// foo = nullptr; // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = MakeRefCounted(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references nullptr. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = MakeRefCounted(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// +// Also see Chromium's ownership and calling conventions: +// https://chromium.googlesource.com/chromium/src/+/lkgr/styleguide/c++/c++.md#object-ownership-and-calling-conventions +// Specifically: +// If the function (at least sometimes) takes a ref on a refcounted object, +// declare the param as scoped_refptr. The caller can decide whether it +// wishes to transfer ownership (by calling std::move(t) when passing t) or +// retain its ref (by simply passing t directly). +// In other words, use scoped_refptr like you would a std::unique_ptr except +// in the odd case where it's required to hold on to a ref while handing one +// to another component (if a component merely needs to use t on the stack +// without keeping a ref: pass t as a raw T*). +template +class scoped_refptr { + public: + typedef T element_type; + + constexpr scoped_refptr() = default; + + // Constructs from raw pointer. constexpr if |p| is null. + constexpr scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + AddRef(ptr_); + } + + // Copy constructor. This is required in addition to the copy conversion + // constructor below. + scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} + + // Copy conversion constructor. + template ::value>::type> + scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} + + // Move constructor. This is required in addition to the move conversion + // constructor below. + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; } + + // Move conversion constructor. + template ::value>::type> + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { + r.ptr_ = nullptr; + } + + ~scoped_refptr() { + static_assert(!base::subtle::IsRefCountPreferenceOverridden( + static_cast(nullptr), static_cast(nullptr)), + "It's unsafe to override the ref count preference." + " Please remove REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE" + " from subclasses."); + if (ptr_) + Release(ptr_); + } + + T* get() const { return ptr_; } + + T& operator*() const { + DCHECK(ptr_); + return *ptr_; + } + + T* operator->() const { + DCHECK(ptr_); + return ptr_; + } + + scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); } + + // Unified assignment operator. + scoped_refptr& operator=(scoped_refptr r) noexcept { + swap(r); + return *this; + } + + void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); } + + explicit operator bool() const { return ptr_ != nullptr; } + + template + bool operator==(const scoped_refptr& rhs) const { + return ptr_ == rhs.get(); + } + + template + bool operator!=(const scoped_refptr& rhs) const { + return !operator==(rhs); + } + + template + bool operator<(const scoped_refptr& rhs) const { + return ptr_ < rhs.get(); + } + + protected: + T* ptr_ = nullptr; + + private: + template + friend scoped_refptr base::AdoptRef(U*); + + scoped_refptr(T* p, base::subtle::AdoptRefTag) : ptr_(p) {} + + // Friend required for move constructors that set r.ptr_ to null. + template + friend class scoped_refptr; + + // Non-inline helpers to allow: + // class Opaque; + // extern template class scoped_refptr; + // Otherwise the compiler will complain that Opaque is an incomplete type. + static void AddRef(T* ptr); + static void Release(T* ptr); +}; + +// static +template +void scoped_refptr::AddRef(T* ptr) { + ptr->AddRef(); +} + +// static +template +void scoped_refptr::Release(T* ptr) { + ptr->Release(); +} + +template +bool operator==(const scoped_refptr& lhs, const U* rhs) { + return lhs.get() == rhs; +} + +template +bool operator==(const T* lhs, const scoped_refptr& rhs) { + return lhs == rhs.get(); +} + +template +bool operator==(const scoped_refptr& lhs, std::nullptr_t null) { + return !static_cast(lhs); +} + +template +bool operator==(std::nullptr_t null, const scoped_refptr& rhs) { + return !static_cast(rhs); +} + +template +bool operator!=(const scoped_refptr& lhs, const U* rhs) { + return !operator==(lhs, rhs); +} + +template +bool operator!=(const T* lhs, const scoped_refptr& rhs) { + return !operator==(lhs, rhs); +} + +template +bool operator!=(const scoped_refptr& lhs, std::nullptr_t null) { + return !operator==(lhs, null); +} + +template +bool operator!=(std::nullptr_t null, const scoped_refptr& rhs) { + return !operator==(null, rhs); +} + +template +std::ostream& operator<<(std::ostream& out, const scoped_refptr& p) { + return out << p.get(); +} + +template +void swap(scoped_refptr& lhs, scoped_refptr& rhs) noexcept { + lhs.swap(rhs); +} + +#endif // BASE_MEMORY_SCOPED_REFPTR_H_ diff --git a/src/3rdparty/gn/base/memory/weak_ptr.cc b/src/3rdparty/gn/base/memory/weak_ptr.cc new file mode 100644 index 00000000000..4abeb7f84ef --- /dev/null +++ b/src/3rdparty/gn/base/memory/weak_ptr.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/weak_ptr.h" + +namespace base { +namespace internal { + +WeakReference::Flag::Flag() : is_valid_(true) {} + +void WeakReference::Flag::Invalidate() { + is_valid_ = false; +} + +bool WeakReference::Flag::IsValid() const { + return is_valid_; +} + +WeakReference::Flag::~Flag() = default; + +WeakReference::WeakReference() = default; + +WeakReference::WeakReference(const scoped_refptr& flag) : flag_(flag) {} + +WeakReference::~WeakReference() = default; + +WeakReference::WeakReference(WeakReference&& other) = default; + +WeakReference::WeakReference(const WeakReference& other) = default; + +bool WeakReference::is_valid() const { + return flag_ && flag_->IsValid(); +} + +WeakReferenceOwner::WeakReferenceOwner() = default; + +WeakReferenceOwner::~WeakReferenceOwner() { + Invalidate(); +} + +WeakReference WeakReferenceOwner::GetRef() const { + // If we hold the last reference to the Flag then create a new one. + if (!HasRefs()) + flag_ = new WeakReference::Flag(); + + return WeakReference(flag_); +} + +void WeakReferenceOwner::Invalidate() { + if (flag_) { + flag_->Invalidate(); + flag_ = nullptr; + } +} + +WeakPtrBase::WeakPtrBase() : ptr_(0) {} + +WeakPtrBase::~WeakPtrBase() = default; + +WeakPtrBase::WeakPtrBase(const WeakReference& ref, uintptr_t ptr) + : ref_(ref), ptr_(ptr) { + DCHECK(ptr_); +} + +WeakPtrFactoryBase::WeakPtrFactoryBase(uintptr_t ptr) : ptr_(ptr) { + DCHECK(ptr_); +} + +WeakPtrFactoryBase::~WeakPtrFactoryBase() { + ptr_ = 0; +} + +} // namespace internal +} // namespace base diff --git a/src/3rdparty/gn/base/memory/weak_ptr.h b/src/3rdparty/gn/base/memory/weak_ptr.h new file mode 100644 index 00000000000..3b2a0af6fb9 --- /dev/null +++ b/src/3rdparty/gn/base/memory/weak_ptr.h @@ -0,0 +1,378 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Weak pointers are pointers to an object that do not affect its lifetime, +// and which may be invalidated (i.e. reset to nullptr) by the object, or its +// owner, at any time, most commonly when the object is about to be deleted. + +// Weak pointers are useful when an object needs to be accessed safely by one +// or more objects other than its owner, and those callers can cope with the +// object vanishing and e.g. tasks posted to it being silently dropped. +// Reference-counting such an object would complicate the ownership graph and +// make it harder to reason about the object's lifetime. + +// EXAMPLE: +// +// class Controller { +// public: +// Controller() : weak_factory_(this) {} +// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); } +// void WorkComplete(const Result& result) { ... } +// private: +// // Member variables should appear before the WeakPtrFactory, to ensure +// // that any WeakPtrs to Controller are invalidated before its members +// // variable's destructors are executed, rendering them invalid. +// WeakPtrFactory weak_factory_; +// }; +// +// class Worker { +// public: +// static void StartNew(const WeakPtr& controller) { +// Worker* worker = new Worker(controller); +// // Kick off asynchronous processing... +// } +// private: +// Worker(const WeakPtr& controller) +// : controller_(controller) {} +// void DidCompleteAsynchronousProcessing(const Result& result) { +// if (controller_) +// controller_->WorkComplete(result); +// } +// WeakPtr controller_; +// }; +// +// With this implementation a caller may use SpawnWorker() to dispatch multiple +// Workers and subsequently delete the Controller, without waiting for all +// Workers to have completed. + +// ------------------------- IMPORTANT: Thread-safety ------------------------- + +// Weak pointers may be passed safely between threads, but must always be +// dereferenced and invalidated on the same SequencedTaskRunner otherwise +// checking the pointer would be racey. +// +// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory +// is dereferenced, the factory and its WeakPtrs become bound to the calling +// thread or current SequencedWorkerPool token, and cannot be dereferenced or +// invalidated on any other task runner. Bound WeakPtrs can still be handed +// off to other task runners, e.g. to use to post tasks back to object on the +// bound sequence. +// +// If all WeakPtr objects are destroyed or invalidated then the factory is +// unbound from the SequencedTaskRunner/Thread. The WeakPtrFactory may then be +// destroyed, or new WeakPtr objects may be used, from a different sequence. +// +// Thus, at least one WeakPtr object must exist and have been dereferenced on +// the correct thread to enforce that other WeakPtr objects will enforce they +// are used on the desired thread. + +#ifndef BASE_MEMORY_WEAK_PTR_H_ +#define BASE_MEMORY_WEAK_PTR_H_ + +#include +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { + +template +class SupportsWeakPtr; +template +class WeakPtr; + +namespace internal { +// These classes are part of the WeakPtr implementation. +// DO NOT USE THESE CLASSES DIRECTLY YOURSELF. + +class WeakReference { + public: + // Although Flag is bound to a specific SequencedTaskRunner, it may be + // deleted from another via base::WeakPtr::~WeakPtr(). + class Flag : public RefCountedThreadSafe { + public: + Flag(); + + void Invalidate(); + bool IsValid() const; + + private: + friend class base::RefCountedThreadSafe; + + ~Flag(); + + bool is_valid_; + }; + + WeakReference(); + explicit WeakReference(const scoped_refptr& flag); + ~WeakReference(); + + WeakReference(WeakReference&& other); + WeakReference(const WeakReference& other); + WeakReference& operator=(WeakReference&& other) = default; + WeakReference& operator=(const WeakReference& other) = default; + + bool is_valid() const; + + private: + scoped_refptr flag_; +}; + +class WeakReferenceOwner { + public: + WeakReferenceOwner(); + ~WeakReferenceOwner(); + + WeakReference GetRef() const; + + bool HasRefs() const { return flag_ && !flag_->HasOneRef(); } + + void Invalidate(); + + private: + mutable scoped_refptr flag_; +}; + +// This class simplifies the implementation of WeakPtr's type conversion +// constructor by avoiding the need for a public accessor for ref_. A +// WeakPtr cannot access the private members of WeakPtr, so this +// base class gives us a way to access ref_ in a protected fashion. +class WeakPtrBase { + public: + WeakPtrBase(); + ~WeakPtrBase(); + + WeakPtrBase(const WeakPtrBase& other) = default; + WeakPtrBase(WeakPtrBase&& other) = default; + WeakPtrBase& operator=(const WeakPtrBase& other) = default; + WeakPtrBase& operator=(WeakPtrBase&& other) = default; + + void reset() { + ref_ = internal::WeakReference(); + ptr_ = 0; + } + + protected: + WeakPtrBase(const WeakReference& ref, uintptr_t ptr); + + WeakReference ref_; + + // This pointer is only valid when ref_.is_valid() is true. Otherwise, its + // value is undefined (as opposed to nullptr). + uintptr_t ptr_; +}; + +// This class provides a common implementation of common functions that would +// otherwise get instantiated separately for each distinct instantiation of +// SupportsWeakPtr<>. +class SupportsWeakPtrBase { + public: + // A safe static downcast of a WeakPtr to WeakPtr. This + // conversion will only compile if there is exists a Base which inherits + // from SupportsWeakPtr. See base::AsWeakPtr() below for a helper + // function that makes calling this easier. + // + // Precondition: t != nullptr + template + static WeakPtr StaticAsWeakPtr(Derived* t) { + static_assert( + std::is_base_of::value, + "AsWeakPtr argument must inherit from SupportsWeakPtr"); + return AsWeakPtrImpl(t); + } + + private: + // This template function uses type inference to find a Base of Derived + // which is an instance of SupportsWeakPtr. We can then safely + // static_cast the Base* to a Derived*. + template + static WeakPtr AsWeakPtrImpl(SupportsWeakPtr* t) { + WeakPtr ptr = t->AsWeakPtr(); + return WeakPtr( + ptr.ref_, static_cast(reinterpret_cast(ptr.ptr_))); + } +}; + +} // namespace internal + +template +class WeakPtrFactory; + +// The WeakPtr class holds a weak reference to |T*|. +// +// This class is designed to be used like a normal pointer. You should always +// null-test an object of this class before using it or invoking a method that +// may result in the underlying object being destroyed. +// +// EXAMPLE: +// +// class Foo { ... }; +// WeakPtr foo; +// if (foo) +// foo->method(); +// +template +class WeakPtr : public internal::WeakPtrBase { + public: + WeakPtr() = default; + + WeakPtr(std::nullptr_t) {} + + // Allow conversion from U to T provided U "is a" T. Note that this + // is separate from the (implicit) copy and move constructors. + template + WeakPtr(const WeakPtr& other) : WeakPtrBase(other) { + // Need to cast from U* to T* to do pointer adjustment in case of multiple + // inheritance. This also enforces the "U is a T" rule. + T* t = reinterpret_cast(other.ptr_); + ptr_ = reinterpret_cast(t); + } + template + WeakPtr(WeakPtr&& other) : WeakPtrBase(std::move(other)) { + // Need to cast from U* to T* to do pointer adjustment in case of multiple + // inheritance. This also enforces the "U is a T" rule. + T* t = reinterpret_cast(other.ptr_); + ptr_ = reinterpret_cast(t); + } + + T* get() const { + return ref_.is_valid() ? reinterpret_cast(ptr_) : nullptr; + } + + T& operator*() const { + DCHECK(get() != nullptr); + return *get(); + } + T* operator->() const { + DCHECK(get() != nullptr); + return get(); + } + + // Allow conditionals to test validity, e.g. if (weak_ptr) {...}; + explicit operator bool() const { return get() != nullptr; } + + private: + friend class internal::SupportsWeakPtrBase; + template + friend class WeakPtr; + friend class SupportsWeakPtr; + friend class WeakPtrFactory; + + WeakPtr(const internal::WeakReference& ref, T* ptr) + : WeakPtrBase(ref, reinterpret_cast(ptr)) {} +}; + +// Allow callers to compare WeakPtrs against nullptr to test validity. +template +bool operator!=(const WeakPtr& weak_ptr, std::nullptr_t) { + return !(weak_ptr == nullptr); +} +template +bool operator!=(std::nullptr_t, const WeakPtr& weak_ptr) { + return weak_ptr != nullptr; +} +template +bool operator==(const WeakPtr& weak_ptr, std::nullptr_t) { + return weak_ptr.get() == nullptr; +} +template +bool operator==(std::nullptr_t, const WeakPtr& weak_ptr) { + return weak_ptr == nullptr; +} + +namespace internal { +class WeakPtrFactoryBase { + protected: + WeakPtrFactoryBase(uintptr_t ptr); + ~WeakPtrFactoryBase(); + internal::WeakReferenceOwner weak_reference_owner_; + uintptr_t ptr_; +}; +} // namespace internal + +// A class may be composed of a WeakPtrFactory and thereby +// control how it exposes weak pointers to itself. This is helpful if you only +// need weak pointers within the implementation of a class. This class is also +// useful when working with primitive types. For example, you could have a +// WeakPtrFactory that is used to pass around a weak reference to a bool. +template +class WeakPtrFactory : public internal::WeakPtrFactoryBase { + public: + explicit WeakPtrFactory(T* ptr) + : WeakPtrFactoryBase(reinterpret_cast(ptr)) {} + + ~WeakPtrFactory() = default; + + WeakPtr GetWeakPtr() { + return WeakPtr(weak_reference_owner_.GetRef(), + reinterpret_cast(ptr_)); + } + + // Call this method to invalidate all existing weak pointers. + void InvalidateWeakPtrs() { + DCHECK(ptr_); + weak_reference_owner_.Invalidate(); + } + + // Call this method to determine if any weak pointers exist. + bool HasWeakPtrs() const { + DCHECK(ptr_); + return weak_reference_owner_.HasRefs(); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory); +}; + +// A class may extend from SupportsWeakPtr to let others take weak pointers to +// it. This avoids the class itself implementing boilerplate to dispense weak +// pointers. However, since SupportsWeakPtr's destructor won't invalidate +// weak pointers to the class until after the derived class' members have been +// destroyed, its use can lead to subtle use-after-destroy issues. +template +class SupportsWeakPtr : public internal::SupportsWeakPtrBase { + public: + SupportsWeakPtr() = default; + + WeakPtr AsWeakPtr() { + return WeakPtr(weak_reference_owner_.GetRef(), static_cast(this)); + } + + protected: + ~SupportsWeakPtr() = default; + + private: + internal::WeakReferenceOwner weak_reference_owner_; + DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr); +}; + +// Helper function that uses type deduction to safely return a WeakPtr +// when Derived doesn't directly extend SupportsWeakPtr, instead it +// extends a Base that extends SupportsWeakPtr. +// +// EXAMPLE: +// class Base : public base::SupportsWeakPtr {}; +// class Derived : public Base {}; +// +// Derived derived; +// base::WeakPtr ptr = base::AsWeakPtr(&derived); +// +// Note that the following doesn't work (invalid type conversion) since +// Derived::AsWeakPtr() is WeakPtr SupportsWeakPtr::AsWeakPtr(), +// and there's no way to safely cast WeakPtr to WeakPtr at +// the caller. +// +// base::WeakPtr ptr = derived.AsWeakPtr(); // Fails. + +template +WeakPtr AsWeakPtr(Derived* t) { + return internal::SupportsWeakPtrBase::StaticAsWeakPtr(t); +} + +} // namespace base + +#endif // BASE_MEMORY_WEAK_PTR_H_ diff --git a/src/3rdparty/gn/base/numerics/checked_math.h b/src/3rdparty/gn/base/numerics/checked_math.h new file mode 100644 index 00000000000..433860c58ba --- /dev/null +++ b/src/3rdparty/gn/base/numerics/checked_math.h @@ -0,0 +1,393 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_CHECKED_MATH_H_ +#define BASE_NUMERICS_CHECKED_MATH_H_ + +#include + +#include +#include + +#include "base/numerics/checked_math_impl.h" + +namespace base { +namespace internal { + +template +class CheckedNumeric { + static_assert(std::is_arithmetic::value, + "CheckedNumeric: T must be a numeric type."); + + public: + using type = T; + + constexpr CheckedNumeric() = default; + + // Copy constructor. + template + constexpr CheckedNumeric(const CheckedNumeric& rhs) + : state_(rhs.state_.value(), rhs.IsValid()) {} + + template + friend class CheckedNumeric; + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template + constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) + : state_(value) { + static_assert(std::is_arithmetic::value, "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template + constexpr CheckedNumeric( + StrictNumeric value) // NOLINT(runtime/explicit) + : state_(static_cast(value)) {} + + // IsValid() - The public API to test if a CheckedNumeric is currently valid. + // A range checked destination type can be supplied using the Dst template + // parameter. + template + constexpr bool IsValid() const { + return state_.is_valid() && + IsValueInRangeForNumericType(state_.value()); + } + + // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid + // and is within the range supported by the destination type. Returns true if + // successful and false otherwise. + template +#if defined(__clang__) || defined(__GNUC__) + __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) + _Check_return_ +#endif + constexpr bool + AssignIfValid(Dst* result) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? ((*result = static_cast(state_.value())), true) + : false; + } + + // ValueOrDie() - The primary accessor for the underlying value. If the + // current state is not valid it will CHECK and crash. + // A range checked destination type can be supplied using the Dst template + // parameter, which will trigger a CHECK if the value is not in bounds for + // the destination. + // The CHECK behavior can be overridden by supplying a handler as a + // template parameter, for test code, etc. However, the handler cannot access + // the underlying value, and it is not available through other means. + template + constexpr StrictNumeric ValueOrDie() const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : CheckHandler::template HandleFailure(); + } + + // ValueOrDefault(T default_value) - A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + // A range checked destination type can be supplied using the Dst template + // parameter. WARNING: This function may fail to compile or CHECK at runtime + // if the supplied default_value is not within range of the destination type. + template + constexpr StrictNumeric ValueOrDefault(const Src default_value) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : checked_cast(default_value); + } + + // Returns a checked numeric of the specified type, cast from the current + // CheckedNumeric. If the current state is invalid or the destination cannot + // represent the result then the returned CheckedNumeric will be invalid. + template + constexpr CheckedNumeric::type> Cast() const { + return *this; + } + + // This friend method is available solely for providing more detailed logging + // in the the tests. Do not implement it in production code, because the + // underlying values may change at any time. + template + friend U GetNumericValueForTest(const CheckedNumeric& src); + + // Prototypes for the supported arithmetic operator overloads. + template + constexpr CheckedNumeric& operator+=(const Src rhs); + template + constexpr CheckedNumeric& operator-=(const Src rhs); + template + constexpr CheckedNumeric& operator*=(const Src rhs); + template + constexpr CheckedNumeric& operator/=(const Src rhs); + template + constexpr CheckedNumeric& operator%=(const Src rhs); + template + constexpr CheckedNumeric& operator<<=(const Src rhs); + template + constexpr CheckedNumeric& operator>>=(const Src rhs); + template + constexpr CheckedNumeric& operator&=(const Src rhs); + template + constexpr CheckedNumeric& operator|=(const Src rhs); + template + constexpr CheckedNumeric& operator^=(const Src rhs); + + constexpr CheckedNumeric operator-() const { + // The negation of two's complement int min is int min, so we simply + // check for that in the constexpr case. + // We use an optimized code path for a known run-time variable. + return MustTreatAsConstexpr(state_.value()) || !std::is_signed::value || + std::is_floating_point::value + ? CheckedNumeric( + NegateWrapper(state_.value()), + IsValid() && (!std::is_signed::value || + std::is_floating_point::value || + NegateWrapper(state_.value()) != + std::numeric_limits::lowest())) + : FastRuntimeNegate(); + } + + constexpr CheckedNumeric operator~() const { + return CheckedNumeric( + InvertWrapper(state_.value()), IsValid()); + } + + constexpr CheckedNumeric Abs() const { + return !IsValueNegative(state_.value()) ? *this : -*this; + } + + template + constexpr CheckedNumeric::type> Max( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsGreater::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } + + template + constexpr CheckedNumeric::type> Min( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsLess::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + constexpr CheckedNumeric::type> + UnsignedAbs() const { + return CheckedNumeric::type>( + SafeUnsignedAbs(state_.value()), state_.is_valid()); + } + + constexpr CheckedNumeric& operator++() { + *this += 1; + return *this; + } + + constexpr CheckedNumeric operator++(int) { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + constexpr CheckedNumeric& operator--() { + *this -= 1; + return *this; + } + + constexpr CheckedNumeric operator--(int) { + CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These perform the actual math operations on the CheckedNumerics. + // Binary arithmetic operations. + template