libv8 8.4.255.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.gitmodules +3 -0
- data/.rspec +3 -0
- data/.travis.yml +45 -0
- data/CHANGELOG.md +111 -0
- data/Gemfile +4 -0
- data/README.md +152 -0
- data/Rakefile +125 -0
- data/appveyor.yml.disabled +36 -0
- data/ext/libv8/arch.rb +20 -0
- data/ext/libv8/builder.rb +106 -0
- data/ext/libv8/extconf.rb +7 -0
- data/ext/libv8/location.rb +89 -0
- data/ext/libv8/paths.rb +28 -0
- data/lib/libv8.rb +9 -0
- data/lib/libv8/version.rb +3 -0
- data/libv8.gemspec +30 -0
- data/scaleway.png +0 -0
- data/spec/location_spec.rb +69 -0
- data/spec/spec_helper.rb +4 -0
- data/thefrontside.png +0 -0
- data/vendor/depot_tools/.cipd_impl.ps1 +129 -0
- data/vendor/depot_tools/.gitattributes +55 -0
- data/vendor/depot_tools/.gitignore +92 -0
- data/vendor/depot_tools/.style.yapf +4 -0
- data/vendor/depot_tools/.vpython +55 -0
- data/vendor/depot_tools/.vpython3 +23 -0
- data/vendor/depot_tools/CROS_OWNERS +7 -0
- data/vendor/depot_tools/GOMA_OWNERS +9 -0
- data/vendor/depot_tools/LICENSE +27 -0
- data/vendor/depot_tools/LUCI_OWNERS +5 -0
- data/vendor/depot_tools/OWNERS +39 -0
- data/vendor/depot_tools/PRESUBMIT.py +150 -0
- data/vendor/depot_tools/README.gclient.md +67 -0
- data/vendor/depot_tools/README.git-cl.md +99 -0
- data/vendor/depot_tools/README.md +78 -0
- data/vendor/depot_tools/WATCHLISTS +26 -0
- data/vendor/depot_tools/auth.py +163 -0
- data/vendor/depot_tools/autoninja +36 -0
- data/vendor/depot_tools/autoninja.bat +33 -0
- data/vendor/depot_tools/autoninja.py +148 -0
- data/vendor/depot_tools/bb +12 -0
- data/vendor/depot_tools/bb.bat +7 -0
- data/vendor/depot_tools/bootstrap/README.md +155 -0
- data/vendor/depot_tools/bootstrap/bootstrap.py +356 -0
- data/vendor/depot_tools/bootstrap/git-bash.template.sh +12 -0
- data/vendor/depot_tools/bootstrap/git.template.bat +5 -0
- data/vendor/depot_tools/bootstrap/manifest.txt +27 -0
- data/vendor/depot_tools/bootstrap/manifest_bleeding_edge.txt +27 -0
- data/vendor/depot_tools/bootstrap/profile.d.python.sh +20 -0
- data/vendor/depot_tools/bootstrap/python27.bat +46 -0
- data/vendor/depot_tools/bootstrap/python3.bat +46 -0
- data/vendor/depot_tools/bootstrap/win_tools.bat +79 -0
- data/vendor/depot_tools/bootstrap_python3 +35 -0
- data/vendor/depot_tools/breakpad.py +12 -0
- data/vendor/depot_tools/cbuildbot +1 -0
- data/vendor/depot_tools/chrome_set_ver +1 -0
- data/vendor/depot_tools/cipd +247 -0
- data/vendor/depot_tools/cipd.bat +67 -0
- data/vendor/depot_tools/cipd_bin_setup.bat +6 -0
- data/vendor/depot_tools/cipd_bin_setup.sh +22 -0
- data/vendor/depot_tools/cipd_client_version +1 -0
- data/vendor/depot_tools/cipd_client_version.digests +22 -0
- data/vendor/depot_tools/cipd_manifest.txt +63 -0
- data/vendor/depot_tools/cipd_manifest.versions +438 -0
- data/vendor/depot_tools/cit +8 -0
- data/vendor/depot_tools/cit.bat +12 -0
- data/vendor/depot_tools/cit.py +167 -0
- data/vendor/depot_tools/clang-format +8 -0
- data/vendor/depot_tools/clang-format.bat +12 -0
- data/vendor/depot_tools/clang_format.py +79 -0
- data/vendor/depot_tools/clang_format_merge_driver +8 -0
- data/vendor/depot_tools/clang_format_merge_driver.bat +12 -0
- data/vendor/depot_tools/clang_format_merge_driver.py +69 -0
- data/vendor/depot_tools/codereview.settings +6 -0
- data/vendor/depot_tools/compile_single_file +8 -0
- data/vendor/depot_tools/compile_single_file.bat +11 -0
- data/vendor/depot_tools/compile_single_file.py +79 -0
- data/vendor/depot_tools/cpplint.bat +11 -0
- data/vendor/depot_tools/cpplint.py +6097 -0
- data/vendor/depot_tools/cpplint_chromium.py +50 -0
- data/vendor/depot_tools/cros +87 -0
- data/vendor/depot_tools/cros_sdk +1 -0
- data/vendor/depot_tools/crosjobs +13 -0
- data/vendor/depot_tools/detect_host_arch.py +55 -0
- data/vendor/depot_tools/dirmd +12 -0
- data/vendor/depot_tools/dirmd.bat +7 -0
- data/vendor/depot_tools/download_from_google_storage +8 -0
- data/vendor/depot_tools/download_from_google_storage.bat +12 -0
- data/vendor/depot_tools/download_from_google_storage.py +634 -0
- data/vendor/depot_tools/ensure_bootstrap +53 -0
- data/vendor/depot_tools/fetch +21 -0
- data/vendor/depot_tools/fetch.bat +28 -0
- data/vendor/depot_tools/fetch.py +319 -0
- data/vendor/depot_tools/fetch_configs/android.py +34 -0
- data/vendor/depot_tools/fetch_configs/android_internal.py +34 -0
- data/vendor/depot_tools/fetch_configs/breakpad.py +44 -0
- data/vendor/depot_tools/fetch_configs/chromium.py +66 -0
- data/vendor/depot_tools/fetch_configs/config_util.py +52 -0
- data/vendor/depot_tools/fetch_configs/crashpad.py +41 -0
- data/vendor/depot_tools/fetch_configs/dart.py +45 -0
- data/vendor/depot_tools/fetch_configs/depot_tools.py +44 -0
- data/vendor/depot_tools/fetch_configs/devtools-frontend.py +44 -0
- data/vendor/depot_tools/fetch_configs/goma_client.py +41 -0
- data/vendor/depot_tools/fetch_configs/gyp.py +41 -0
- data/vendor/depot_tools/fetch_configs/infra.py +40 -0
- data/vendor/depot_tools/fetch_configs/infra_internal.py +45 -0
- data/vendor/depot_tools/fetch_configs/inspector_protocol.py +40 -0
- data/vendor/depot_tools/fetch_configs/ios.py +34 -0
- data/vendor/depot_tools/fetch_configs/ios_internal.py +39 -0
- data/vendor/depot_tools/fetch_configs/nacl.py +48 -0
- data/vendor/depot_tools/fetch_configs/naclports.py +47 -0
- data/vendor/depot_tools/fetch_configs/node-ci.py +41 -0
- data/vendor/depot_tools/fetch_configs/pdfium.py +40 -0
- data/vendor/depot_tools/fetch_configs/skia.py +41 -0
- data/vendor/depot_tools/fetch_configs/skia_buildbot.py +41 -0
- data/vendor/depot_tools/fetch_configs/syzygy.py +41 -0
- data/vendor/depot_tools/fetch_configs/v8.py +44 -0
- data/vendor/depot_tools/fetch_configs/webrtc.py +52 -0
- data/vendor/depot_tools/fetch_configs/webrtc_android.py +34 -0
- data/vendor/depot_tools/fetch_configs/webrtc_ios.py +34 -0
- data/vendor/depot_tools/fix_encoding.py +385 -0
- data/vendor/depot_tools/gclient +38 -0
- data/vendor/depot_tools/gclient-new-workdir.py +124 -0
- data/vendor/depot_tools/gclient.bat +32 -0
- data/vendor/depot_tools/gclient.py +3198 -0
- data/vendor/depot_tools/gclient_completion.sh +76 -0
- data/vendor/depot_tools/gclient_eval.py +891 -0
- data/vendor/depot_tools/gclient_paths.py +152 -0
- data/vendor/depot_tools/gclient_scm.py +1615 -0
- data/vendor/depot_tools/gclient_utils.py +1280 -0
- data/vendor/depot_tools/gerrit_client.py +151 -0
- data/vendor/depot_tools/gerrit_util.py +996 -0
- data/vendor/depot_tools/git-cache +6 -0
- data/vendor/depot_tools/git-cl +6 -0
- data/vendor/depot_tools/git-crrev-parse +53 -0
- data/vendor/depot_tools/git-drover +6 -0
- data/vendor/depot_tools/git-find-releases +6 -0
- data/vendor/depot_tools/git-footers +6 -0
- data/vendor/depot_tools/git-freeze +8 -0
- data/vendor/depot_tools/git-gs +9 -0
- data/vendor/depot_tools/git-hyper-blame +6 -0
- data/vendor/depot_tools/git-map +6 -0
- data/vendor/depot_tools/git-map-branches +6 -0
- data/vendor/depot_tools/git-mark-merge-base +6 -0
- data/vendor/depot_tools/git-nav-downstream +6 -0
- data/vendor/depot_tools/git-nav-upstream +6 -0
- data/vendor/depot_tools/git-new-branch +6 -0
- data/vendor/depot_tools/git-number +6 -0
- data/vendor/depot_tools/git-rebase-update +6 -0
- data/vendor/depot_tools/git-rename-branch +6 -0
- data/vendor/depot_tools/git-reparent-branch +6 -0
- data/vendor/depot_tools/git-retry +8 -0
- data/vendor/depot_tools/git-runhooks +23 -0
- data/vendor/depot_tools/git-squash-branch +6 -0
- data/vendor/depot_tools/git-templates/description +3 -0
- data/vendor/depot_tools/git-templates/hooks/applypatch-msg +4 -0
- data/vendor/depot_tools/git-templates/hooks/post-applypatch +4 -0
- data/vendor/depot_tools/git-templates/hooks/post-checkout +4 -0
- data/vendor/depot_tools/git-templates/hooks/post-commit +4 -0
- data/vendor/depot_tools/git-templates/hooks/post-merge +4 -0
- data/vendor/depot_tools/git-templates/hooks/post-update +4 -0
- data/vendor/depot_tools/git-templates/hooks/pre-applypatch +4 -0
- data/vendor/depot_tools/git-templates/hooks/pre-auto-gc +4 -0
- data/vendor/depot_tools/git-templates/hooks/pre-commit +4 -0
- data/vendor/depot_tools/git-templates/hooks/pre-rebase +4 -0
- data/vendor/depot_tools/git-templates/hooks/prepare-commit-msg +4 -0
- data/vendor/depot_tools/git-templates/info/exclude +6 -0
- data/vendor/depot_tools/git-thaw +13 -0
- data/vendor/depot_tools/git-upstream-diff +9 -0
- data/vendor/depot_tools/git_cache.py +786 -0
- data/vendor/depot_tools/git_cl.py +5158 -0
- data/vendor/depot_tools/git_cl_completion.sh +48 -0
- data/vendor/depot_tools/git_common.py +1101 -0
- data/vendor/depot_tools/git_dates.py +62 -0
- data/vendor/depot_tools/git_drover.py +469 -0
- data/vendor/depot_tools/git_find_releases.py +67 -0
- data/vendor/depot_tools/git_footers.py +261 -0
- data/vendor/depot_tools/git_freezer.py +40 -0
- data/vendor/depot_tools/git_hyper_blame.py +391 -0
- data/vendor/depot_tools/git_map.py +166 -0
- data/vendor/depot_tools/git_map_branches.py +354 -0
- data/vendor/depot_tools/git_mark_merge_base.py +73 -0
- data/vendor/depot_tools/git_nav_downstream.py +69 -0
- data/vendor/depot_tools/git_new_branch.py +82 -0
- data/vendor/depot_tools/git_number.py +301 -0
- data/vendor/depot_tools/git_rebase_update.py +351 -0
- data/vendor/depot_tools/git_rename_branch.py +55 -0
- data/vendor/depot_tools/git_reparent_branch.py +101 -0
- data/vendor/depot_tools/git_retry.py +181 -0
- data/vendor/depot_tools/git_squash_branch.py +28 -0
- data/vendor/depot_tools/git_upstream_diff.py +64 -0
- data/vendor/depot_tools/gn +8 -0
- data/vendor/depot_tools/gn.bat +12 -0
- data/vendor/depot_tools/gn.py +78 -0
- data/vendor/depot_tools/goma_auth +12 -0
- data/vendor/depot_tools/goma_auth.bat +8 -0
- data/vendor/depot_tools/goma_ctl +12 -0
- data/vendor/depot_tools/goma_ctl.bat +8 -0
- data/vendor/depot_tools/gsutil.py +190 -0
- data/vendor/depot_tools/gsutil.py.bat +23 -0
- data/vendor/depot_tools/gsutil.vpython +120 -0
- data/vendor/depot_tools/infra/README.md +1 -0
- data/vendor/depot_tools/infra/config/OWNERS +7 -0
- data/vendor/depot_tools/infra/config/README.md +1 -0
- data/vendor/depot_tools/infra/config/recipes.cfg +26 -0
- data/vendor/depot_tools/led +12 -0
- data/vendor/depot_tools/led.bat +7 -0
- data/vendor/depot_tools/lockfile.py +116 -0
- data/vendor/depot_tools/luci-auth +13 -0
- data/vendor/depot_tools/luci-auth.bat +8 -0
- data/vendor/depot_tools/lucicfg +12 -0
- data/vendor/depot_tools/lucicfg.bat +7 -0
- data/vendor/depot_tools/mac_toolchain +12 -0
- data/vendor/depot_tools/man/html/depot_tools.html +934 -0
- data/vendor/depot_tools/man/html/depot_tools_tutorial.html +1593 -0
- data/vendor/depot_tools/man/html/git-cl.html +1033 -0
- data/vendor/depot_tools/man/html/git-drover.html +1048 -0
- data/vendor/depot_tools/man/html/git-footers.html +878 -0
- data/vendor/depot_tools/man/html/git-freeze.html +859 -0
- data/vendor/depot_tools/man/html/git-hyper-blame.html +878 -0
- data/vendor/depot_tools/man/html/git-map-branches.html +904 -0
- data/vendor/depot_tools/man/html/git-map.html +887 -0
- data/vendor/depot_tools/man/html/git-mark-merge-base.html +826 -0
- data/vendor/depot_tools/man/html/git-nav-downstream.html +844 -0
- data/vendor/depot_tools/man/html/git-nav-upstream.html +853 -0
- data/vendor/depot_tools/man/html/git-new-branch.html +932 -0
- data/vendor/depot_tools/man/html/git-rebase-update.html +961 -0
- data/vendor/depot_tools/man/html/git-rename-branch.html +794 -0
- data/vendor/depot_tools/man/html/git-reparent-branch.html +847 -0
- data/vendor/depot_tools/man/html/git-retry.html +858 -0
- data/vendor/depot_tools/man/html/git-squash-branch.html +881 -0
- data/vendor/depot_tools/man/html/git-thaw.html +794 -0
- data/vendor/depot_tools/man/html/git-upstream-diff.html +923 -0
- data/vendor/depot_tools/man/man1/git-cl.1 +198 -0
- data/vendor/depot_tools/man/man1/git-drover.1 +330 -0
- data/vendor/depot_tools/man/man1/git-footers.1 +144 -0
- data/vendor/depot_tools/man/man1/git-freeze.1 +113 -0
- data/vendor/depot_tools/man/man1/git-hyper-blame.1 +128 -0
- data/vendor/depot_tools/man/man1/git-map-branches.1 +210 -0
- data/vendor/depot_tools/man/man1/git-map.1 +179 -0
- data/vendor/depot_tools/man/man1/git-mark-merge-base.1 +69 -0
- data/vendor/depot_tools/man/man1/git-nav-downstream.1 +112 -0
- data/vendor/depot_tools/man/man1/git-nav-upstream.1 +122 -0
- data/vendor/depot_tools/man/man1/git-new-branch.1 +176 -0
- data/vendor/depot_tools/man/man1/git-rebase-update.1 +177 -0
- data/vendor/depot_tools/man/man1/git-rename-branch.1 +53 -0
- data/vendor/depot_tools/man/man1/git-reparent-branch.1 +93 -0
- data/vendor/depot_tools/man/man1/git-retry.1 +108 -0
- data/vendor/depot_tools/man/man1/git-squash-branch.1 +129 -0
- data/vendor/depot_tools/man/man1/git-thaw.1 +54 -0
- data/vendor/depot_tools/man/man1/git-upstream-diff.1 +153 -0
- data/vendor/depot_tools/man/man7/depot_tools.7 +139 -0
- data/vendor/depot_tools/man/man7/depot_tools_tutorial.7 +1061 -0
- data/vendor/depot_tools/man/push_to_gs.sh +4 -0
- data/vendor/depot_tools/man/src/.gitignore +5 -0
- data/vendor/depot_tools/man/src/_aliases.txt +5 -0
- data/vendor/depot_tools/man/src/_footer.txt +8 -0
- data/vendor/depot_tools/man/src/_git-cl_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-drover_desc.helper.txt +2 -0
- data/vendor/depot_tools/man/src/_git-footers_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-freeze_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-hyper-blame_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-map-branches_desc.helper.txt +4 -0
- data/vendor/depot_tools/man/src/_git-map_desc.helper.txt +3 -0
- data/vendor/depot_tools/man/src/_git-mark-merge-base_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-nav-downstream_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-nav-upstream_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-new-branch_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-rebase-update_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-rename-branch_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-reparent-branch_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-retry_desc.helper.txt +2 -0
- data/vendor/depot_tools/man/src/_git-squash-branch_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_git-thaw_desc.helper.txt +2 -0
- data/vendor/depot_tools/man/src/_git-upstream-diff_desc.helper.txt +1 -0
- data/vendor/depot_tools/man/src/_helper_prefix.txt +1 -0
- data/vendor/depot_tools/man/src/asciidoc-override.css +7 -0
- data/vendor/depot_tools/man/src/common_demo_functions.sh +84 -0
- data/vendor/depot_tools/man/src/demo_repo.sh +43 -0
- data/vendor/depot_tools/man/src/depot_tools.txt +28 -0
- data/vendor/depot_tools/man/src/depot_tools_tutorial.demo.walkthrough.sh +155 -0
- data/vendor/depot_tools/man/src/depot_tools_tutorial.txt +432 -0
- data/vendor/depot_tools/man/src/filter_demo_output.py +141 -0
- data/vendor/depot_tools/man/src/git-cl.txt +119 -0
- data/vendor/depot_tools/man/src/git-drover.demo.1.sh +22 -0
- data/vendor/depot_tools/man/src/git-drover.demo.2.sh +23 -0
- data/vendor/depot_tools/man/src/git-drover.demo.3.sh +27 -0
- data/vendor/depot_tools/man/src/git-drover.demo.4.sh +39 -0
- data/vendor/depot_tools/man/src/git-drover.demo.common.sh +19 -0
- data/vendor/depot_tools/man/src/git-drover.txt +102 -0
- data/vendor/depot_tools/man/src/git-footers.demo.1.sh +17 -0
- data/vendor/depot_tools/man/src/git-footers.txt +71 -0
- data/vendor/depot_tools/man/src/git-freeze.demo.1.sh +23 -0
- data/vendor/depot_tools/man/src/git-freeze.txt +54 -0
- data/vendor/depot_tools/man/src/git-hyper-blame.demo.1.sh +3 -0
- data/vendor/depot_tools/man/src/git-hyper-blame.demo.2.sh +4 -0
- data/vendor/depot_tools/man/src/git-hyper-blame.demo.common.sh +57 -0
- data/vendor/depot_tools/man/src/git-hyper-blame.txt +85 -0
- data/vendor/depot_tools/man/src/git-map-branches.demo.1.sh +8 -0
- data/vendor/depot_tools/man/src/git-map-branches.txt +64 -0
- data/vendor/depot_tools/man/src/git-map.demo.1.sh +3 -0
- data/vendor/depot_tools/man/src/git-map.txt +67 -0
- data/vendor/depot_tools/man/src/git-mark-merge-base.txt +46 -0
- data/vendor/depot_tools/man/src/git-nav-downstream.demo.1.sh +12 -0
- data/vendor/depot_tools/man/src/git-nav-downstream.txt +40 -0
- data/vendor/depot_tools/man/src/git-nav-upstream.demo.1.sh +12 -0
- data/vendor/depot_tools/man/src/git-nav-upstream.txt +39 -0
- data/vendor/depot_tools/man/src/git-new-branch.demo.1.sh +17 -0
- data/vendor/depot_tools/man/src/git-new-branch.txt +82 -0
- data/vendor/depot_tools/man/src/git-rebase-update.txt +141 -0
- data/vendor/depot_tools/man/src/git-rename-branch.txt +28 -0
- data/vendor/depot_tools/man/src/git-reparent-branch.txt +61 -0
- data/vendor/depot_tools/man/src/git-retry.txt +67 -0
- data/vendor/depot_tools/man/src/git-squash-branch.demo.1.sh +12 -0
- data/vendor/depot_tools/man/src/git-squash-branch.txt +59 -0
- data/vendor/depot_tools/man/src/git-thaw.txt +29 -0
- data/vendor/depot_tools/man/src/git-upstream-diff.txt +107 -0
- data/vendor/depot_tools/man/src/make_docs.sh +260 -0
- data/vendor/depot_tools/man/src/prep_demo_repo.sh +103 -0
- data/vendor/depot_tools/metrics.README.md +101 -0
- data/vendor/depot_tools/metrics.py +297 -0
- data/vendor/depot_tools/metrics_utils.py +293 -0
- data/vendor/depot_tools/my_activity.py +971 -0
- data/vendor/depot_tools/ninja +44 -0
- data/vendor/depot_tools/ninja-linux32 +0 -0
- data/vendor/depot_tools/ninja-linux64 +0 -0
- data/vendor/depot_tools/ninja-mac +0 -0
- data/vendor/depot_tools/ninja.exe +0 -0
- data/vendor/depot_tools/ninjalog.README.md +64 -0
- data/vendor/depot_tools/ninjalog_uploader.py +233 -0
- data/vendor/depot_tools/ninjalog_uploader_wrapper.py +125 -0
- data/vendor/depot_tools/owners.py +641 -0
- data/vendor/depot_tools/owners_finder.py +385 -0
- data/vendor/depot_tools/post_build_ninja_summary.py +350 -0
- data/vendor/depot_tools/presubmit_canned_checks.py +1655 -0
- data/vendor/depot_tools/presubmit_support.py +1923 -0
- data/vendor/depot_tools/profile.xml +8 -0
- data/vendor/depot_tools/prpc +13 -0
- data/vendor/depot_tools/prpc.bat +8 -0
- data/vendor/depot_tools/pylint +9 -0
- data/vendor/depot_tools/pylint-1.5 +78 -0
- data/vendor/depot_tools/pylint-1.6 +78 -0
- data/vendor/depot_tools/pylint-1.7 +78 -0
- data/vendor/depot_tools/pylint-1.8 +78 -0
- data/vendor/depot_tools/pylint-1.9 +78 -0
- data/vendor/depot_tools/pylint.bat +12 -0
- data/vendor/depot_tools/pylint_main.py +45 -0
- data/vendor/depot_tools/pylintrc +349 -0
- data/vendor/depot_tools/python-bin/python3 +7 -0
- data/vendor/depot_tools/python_runner.sh +60 -0
- data/vendor/depot_tools/rdb +12 -0
- data/vendor/depot_tools/rdb.bat +7 -0
- data/vendor/depot_tools/recipes/OWNERS +4 -0
- data/vendor/depot_tools/recipes/README.recipes.md +1079 -0
- data/vendor/depot_tools/recipes/recipe_modules/OWNERS +1 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/OWNERS +2 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/__init__.py +38 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/api.py +516 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic.json +117 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic_luci.json +117 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic_with_branch_heads.json +118 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/clobber.json +211 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/deprecated_got_revision_mapping.json +194 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/gerrit_no_rebase_patch_ref.json +211 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/gerrit_no_reset.json +211 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/input_commit_with_id_without_repo.json +210 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/multiple_patch_refs.json +214 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/no_apply_patch_on_gclient.json +265 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/no_cp_checkout_HEAD.json +65 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/no_cp_checkout_a_branch_head.json +65 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/no_cp_checkout_a_specific_commit.json +65 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/no_cp_checkout_master.json +65 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/refs.json +212 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/reset_root_solution_revision.json +210 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/resolve_chromium_fixed_version.json +117 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail.json +95 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch.json +209 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch_download.json +193 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_angle.json +265 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_branch_heads.json +261 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_feature_branch.json +261 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_v8_feature_branch.json +265 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_webrtc.json +265 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8.json +265 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8_head_by_default.json +265 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/unrecognized_commit_repo.json +8 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/with_tags.json +211 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.py +308 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/resources/__init__.py +0 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/resources/bot_update.py +1258 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/test_api.py +93 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/tests/do_not_retry_patch_failures_in_cq.py +42 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/tests/ensure_checkout.py +35 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/__init__.py +9 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/api.py +455 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/basic.json +403 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/basic_pkg.json +403 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/describe-failed.json +82 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/describe-many-instances.json +411 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/mac64.json +403 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/pkg_bad_file.json +315 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/pkg_bad_mode.json +313 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/pkg_bad_verfile.json +313 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/win64.json +403 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.py +187 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/junk arch.json +5 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/junk bits.json +5 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_arm_32.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_arm_64.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_intel_32.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_intel_64.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_mips_64.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/mac_intel_64.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/win_intel_32.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/win_intel_64.json +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.py +59 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/test_api.py +93 -0
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/__init__.py +9 -0
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/api.py +75 -0
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.expected/basic.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.expected/basic_luci.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.expected/win.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.py +55 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/__init__.py +13 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/api.py +428 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/config.py +462 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/basic.json +238 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/revision.json +240 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/tryserver.json +238 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.py +95 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/resources/diff_deps.py +16 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/test_api.py +36 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/diff_deps.expected/basic.json +55 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/diff_deps.expected/dont have revision yet.json +84 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/diff_deps.expected/no change, exception.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/diff_deps.expected/windows.json +55 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/diff_deps.py +88 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/patch_project.py +92 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/sync_failure.py +24 -0
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/__init__.py +8 -0
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/api.py +178 -0
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/examples/full.expected/basic.json +284 -0
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/examples/full.py +73 -0
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/test_api.py +53 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/__init__.py +11 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/api.py +405 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic.json +217 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_branch.json +217 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_file_name.json +219 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_hash.json +216 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_ref.json +217 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_submodule_update_force.json +218 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_tags.json +218 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/can_fail_build.json +164 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/cannot_fail_build.json +220 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/cat-file_test.json +239 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_delta.json +290 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_failed.json +220 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_with_bad_output.json +222 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_with_bad_output_fails_build.json +111 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/curl_trace_file.json +218 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/git-cache-checkout.json +265 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/platform_win.json +217 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/rebase_failed.json +221 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/remote_not_origin.json +219 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/set_got_revision.json +218 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.py +170 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/resources/git_setup.py +52 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/test_api.py +18 -0
- data/vendor/depot_tools/recipes/recipe_modules/git_cl/__init__.py +5 -0
- data/vendor/depot_tools/recipes/recipe_modules/git_cl/api.py +50 -0
- data/vendor/depot_tools/recipes/recipe_modules/git_cl/config.py +22 -0
- data/vendor/depot_tools/recipes/recipe_modules/git_cl/examples/full.expected/basic.json +105 -0
- data/vendor/depot_tools/recipes/recipe_modules/git_cl/examples/full.py +47 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/OWNERS +2 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/__init__.py +12 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/api.py +257 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/examples/full.expected/basic.json +596 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/examples/full.py +93 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/resources/gerrit_client.py +251 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/test_api.py +95 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/basic.json +5 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/tests/parse_repo_url.py +49 -0
- data/vendor/depot_tools/recipes/recipe_modules/gsutil/__init__.py +4 -0
- data/vendor/depot_tools/recipes/recipe_modules/gsutil/api.py +222 -0
- data/vendor/depot_tools/recipes/recipe_modules/gsutil/examples/full.expected/basic.json +247 -0
- data/vendor/depot_tools/recipes/recipe_modules/gsutil/examples/full.py +92 -0
- data/vendor/depot_tools/recipes/recipe_modules/gsutil/resources/gsutil_smart_retry.py +69 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/__init__.py +36 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/api.py +140 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/ancient_version.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/automatic_version.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/explicit_version.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/linux.json +21 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/mac.json +83 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/win.json +21 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.py +42 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/__init__.py +27 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/api.py +251 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/examples/full.expected/basic.json +27 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/examples/full.py +19 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/properties.proto +14 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/test_api.py +19 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/tests/execute.py +247 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/tests/prepare.py +49 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/__init__.py +18 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/api.py +264 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/basic_tags.json +57 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_gerrit_patch.json +190 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_gerrit_patch_and_target_ref.json +190 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_wrong_patch.json +21 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_wrong_patch_new.json +22 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.py +95 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/test_api.py +14 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/tests/gerrit_change_fetch_ref_timeout.py +29 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/tests/gerrit_change_owner.expected/basic.json +5 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/tests/gerrit_change_owner.py +22 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/tests/gerrit_change_target_ref.py +32 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/__init__.py +25 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/api.py +137 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.expected/linux.json +21 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.expected/mac.json +21 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.expected/win.json +108 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.py +21 -0
- data/vendor/depot_tools/recipes/recipes.py +249 -0
- data/vendor/depot_tools/recipes/recipes/fetch_end_to_end_test.expected/basic.json +175 -0
- data/vendor/depot_tools/recipes/recipes/fetch_end_to_end_test.py +56 -0
- data/vendor/depot_tools/recipes/trigger_recipe_roller.txt +13 -0
- data/vendor/depot_tools/repo +1194 -0
- data/vendor/depot_tools/roll-dep +21 -0
- data/vendor/depot_tools/roll-dep.bat +21 -0
- data/vendor/depot_tools/roll_dep.py +282 -0
- data/vendor/depot_tools/scm.py +415 -0
- data/vendor/depot_tools/setup_color.py +130 -0
- data/vendor/depot_tools/split_cl.py +263 -0
- data/vendor/depot_tools/subcommand.py +261 -0
- data/vendor/depot_tools/subprocess2.py +258 -0
- data/vendor/depot_tools/third_party/__init__.py +5 -0
- data/vendor/depot_tools/third_party/colorama/LICENSE.txt +27 -0
- data/vendor/depot_tools/third_party/colorama/README.chromium +12 -0
- data/vendor/depot_tools/third_party/colorama/README.rst +346 -0
- data/vendor/depot_tools/third_party/colorama/__init__.py +6 -0
- data/vendor/depot_tools/third_party/colorama/ansi.py +102 -0
- data/vendor/depot_tools/third_party/colorama/ansitowin32.py +257 -0
- data/vendor/depot_tools/third_party/colorama/initialise.py +80 -0
- data/vendor/depot_tools/third_party/colorama/win32.py +152 -0
- data/vendor/depot_tools/third_party/colorama/winterm.py +169 -0
- data/vendor/depot_tools/third_party/coverage/AUTHORS.txt +43 -0
- data/vendor/depot_tools/third_party/coverage/PKG-INFO +41 -0
- data/vendor/depot_tools/third_party/coverage/README.chromium +13 -0
- data/vendor/depot_tools/third_party/coverage/__init__.py +120 -0
- data/vendor/depot_tools/third_party/coverage/__main__.py +4 -0
- data/vendor/depot_tools/third_party/coverage/annotate.py +101 -0
- data/vendor/depot_tools/third_party/coverage/backward.py +184 -0
- data/vendor/depot_tools/third_party/coverage/bytecode.py +75 -0
- data/vendor/depot_tools/third_party/coverage/cmdline.py +740 -0
- data/vendor/depot_tools/third_party/coverage/codeunit.py +145 -0
- data/vendor/depot_tools/third_party/coverage/collector.py +353 -0
- data/vendor/depot_tools/third_party/coverage/config.py +213 -0
- data/vendor/depot_tools/third_party/coverage/control.py +776 -0
- data/vendor/depot_tools/third_party/coverage/data.py +278 -0
- data/vendor/depot_tools/third_party/coverage/debug.py +54 -0
- data/vendor/depot_tools/third_party/coverage/execfile.py +171 -0
- data/vendor/depot_tools/third_party/coverage/files.py +309 -0
- data/vendor/depot_tools/third_party/coverage/fullcoverage/encodings.py +57 -0
- data/vendor/depot_tools/third_party/coverage/html.py +387 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/coverage_html.js +376 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/index.html +104 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/jquery-1.4.3.min.js +166 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/jquery.hotkeys.js +99 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/jquery.isonscreen.js +53 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/jquery.min.js +166 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/jquery.tablesorter.min.js +2 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/keybd_closed.png +0 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/keybd_open.png +0 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/pyfile.html +90 -0
- data/vendor/depot_tools/third_party/coverage/htmlfiles/style.css +300 -0
- data/vendor/depot_tools/third_party/coverage/misc.py +163 -0
- data/vendor/depot_tools/third_party/coverage/parser.py +666 -0
- data/vendor/depot_tools/third_party/coverage/phystokens.py +208 -0
- data/vendor/depot_tools/third_party/coverage/report.py +92 -0
- data/vendor/depot_tools/third_party/coverage/results.py +286 -0
- data/vendor/depot_tools/third_party/coverage/summary.py +86 -0
- data/vendor/depot_tools/third_party/coverage/templite.py +166 -0
- data/vendor/depot_tools/third_party/coverage/version.py +9 -0
- data/vendor/depot_tools/third_party/coverage/xmlreport.py +155 -0
- data/vendor/depot_tools/third_party/httplib2/LICENSE +1339 -0
- data/vendor/depot_tools/third_party/httplib2/README.chromium +15 -0
- data/vendor/depot_tools/third_party/httplib2/__init__.py +1780 -0
- data/vendor/depot_tools/third_party/httplib2/cacerts.txt +2196 -0
- data/vendor/depot_tools/third_party/httplib2/iri2uri.py +110 -0
- data/vendor/depot_tools/third_party/httplib2/socks.py +448 -0
- data/vendor/depot_tools/third_party/repo/COPYING +202 -0
- data/vendor/depot_tools/third_party/repo/README.chromium +4 -0
- data/vendor/depot_tools/third_party/repo/__init__.py +0 -0
- data/vendor/depot_tools/third_party/repo/progress.py +117 -0
- data/vendor/depot_tools/third_party/retry_decorator/LICENSE.google +30 -0
- data/vendor/depot_tools/third_party/retry_decorator/__init__.py +0 -0
- data/vendor/depot_tools/third_party/retry_decorator/decorators.py +45 -0
- data/vendor/depot_tools/third_party/schema/.editorconfig +15 -0
- data/vendor/depot_tools/third_party/schema/.gitignore +174 -0
- data/vendor/depot_tools/third_party/schema/.travis.yml +37 -0
- data/vendor/depot_tools/third_party/schema/LICENSE-MIT +19 -0
- data/vendor/depot_tools/third_party/schema/MANIFEST.in +1 -0
- data/vendor/depot_tools/third_party/schema/README.chromium +12 -0
- data/vendor/depot_tools/third_party/schema/README.rst +382 -0
- data/vendor/depot_tools/third_party/schema/__init__.py +1 -0
- data/vendor/depot_tools/third_party/schema/schema.py +338 -0
- data/vendor/depot_tools/third_party/schema/setup.cfg +5 -0
- data/vendor/depot_tools/third_party/schema/setup.py +30 -0
- data/vendor/depot_tools/third_party/schema/test_schema.py +556 -0
- data/vendor/depot_tools/third_party/schema/tox.ini +33 -0
- data/vendor/depot_tools/third_party/six/LICENSE.txt +18 -0
- data/vendor/depot_tools/third_party/six/README.chromium +10 -0
- data/vendor/depot_tools/third_party/six/__init__.py +762 -0
- data/vendor/depot_tools/update_depot_tools +138 -0
- data/vendor/depot_tools/update_depot_tools.bat +65 -0
- data/vendor/depot_tools/update_depot_tools_toggle.py +38 -0
- data/vendor/depot_tools/upload_metrics.py +26 -0
- data/vendor/depot_tools/upload_to_google_storage.py +306 -0
- data/vendor/depot_tools/vpython +42 -0
- data/vendor/depot_tools/vpython.bat +7 -0
- data/vendor/depot_tools/vpython3 +55 -0
- data/vendor/depot_tools/vpython3.bat +12 -0
- data/vendor/depot_tools/watchlists.py +141 -0
- data/vendor/depot_tools/weekly +54 -0
- data/vendor/depot_tools/win32imports.py +61 -0
- data/vendor/depot_tools/win_toolchain/OWNERS +2 -0
- data/vendor/depot_tools/win_toolchain/README.md +74 -0
- data/vendor/depot_tools/win_toolchain/get_toolchain_if_necessary.py +599 -0
- data/vendor/depot_tools/win_toolchain/package_from_installed.py +524 -0
- data/vendor/depot_tools/wtf +81 -0
- data/vendor/depot_tools/yapf +21 -0
- data/vendor/depot_tools/yapf.bat +12 -0
- data/vendor/depot_tools/zsh-goodies/README +6 -0
- data/vendor/depot_tools/zsh-goodies/_gclient +14 -0
- metadata +729 -0
@@ -0,0 +1,1923 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
3
|
+
# Use of this source code is governed by a BSD-style license that can be
|
4
|
+
# found in the LICENSE file.
|
5
|
+
|
6
|
+
"""Enables directory-specific presubmit checks to run at upload and/or commit.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from __future__ import print_function
|
10
|
+
|
11
|
+
__version__ = '1.8.0'
|
12
|
+
|
13
|
+
# TODO(joi) Add caching where appropriate/needed. The API is designed to allow
|
14
|
+
# caching (between all different invocations of presubmit scripts for a given
|
15
|
+
# change). We should add it as our presubmit scripts start feeling slow.
|
16
|
+
|
17
|
+
import argparse
|
18
|
+
import ast # Exposed through the API.
|
19
|
+
import contextlib
|
20
|
+
import cpplint
|
21
|
+
import fnmatch # Exposed through the API.
|
22
|
+
import glob
|
23
|
+
import inspect
|
24
|
+
import itertools
|
25
|
+
import json # Exposed through the API.
|
26
|
+
import logging
|
27
|
+
import multiprocessing
|
28
|
+
import os # Somewhat exposed through the API.
|
29
|
+
import random
|
30
|
+
import re # Exposed through the API.
|
31
|
+
import signal
|
32
|
+
import sys # Parts exposed through API.
|
33
|
+
import tempfile # Exposed through the API.
|
34
|
+
import threading
|
35
|
+
import time
|
36
|
+
import traceback
|
37
|
+
import unittest # Exposed through the API.
|
38
|
+
from warnings import warn
|
39
|
+
|
40
|
+
# Local imports.
|
41
|
+
import fix_encoding
|
42
|
+
import gclient_paths # Exposed through the API
|
43
|
+
import gclient_utils
|
44
|
+
import git_footers
|
45
|
+
import gerrit_util
|
46
|
+
import owners
|
47
|
+
import owners_finder
|
48
|
+
import presubmit_canned_checks
|
49
|
+
import scm
|
50
|
+
import subprocess2 as subprocess # Exposed through the API.
|
51
|
+
|
52
|
+
if sys.version_info.major == 2:
|
53
|
+
# TODO(1009814): Expose urllib2 only through urllib_request and urllib_error
|
54
|
+
import urllib2 # Exposed through the API.
|
55
|
+
import urlparse
|
56
|
+
import urllib2 as urllib_request
|
57
|
+
import urllib2 as urllib_error
|
58
|
+
else:
|
59
|
+
import urllib.parse as urlparse
|
60
|
+
import urllib.request as urllib_request
|
61
|
+
import urllib.error as urllib_error
|
62
|
+
|
63
|
+
# Ask for feedback only once in program lifetime.
|
64
|
+
_ASKED_FOR_FEEDBACK = False
|
65
|
+
|
66
|
+
|
67
|
+
def time_time():
|
68
|
+
# Use this so that it can be mocked in tests without interfering with python
|
69
|
+
# system machinery.
|
70
|
+
return time.time()
|
71
|
+
|
72
|
+
|
73
|
+
class PresubmitFailure(Exception):
|
74
|
+
pass
|
75
|
+
|
76
|
+
|
77
|
+
class CommandData(object):
|
78
|
+
def __init__(self, name, cmd, kwargs, message, python3=False):
|
79
|
+
self.name = name
|
80
|
+
self.cmd = cmd
|
81
|
+
self.stdin = kwargs.get('stdin', None)
|
82
|
+
self.kwargs = kwargs.copy()
|
83
|
+
self.kwargs['stdout'] = subprocess.PIPE
|
84
|
+
self.kwargs['stderr'] = subprocess.STDOUT
|
85
|
+
self.kwargs['stdin'] = subprocess.PIPE
|
86
|
+
self.message = message
|
87
|
+
self.info = None
|
88
|
+
self.python3 = python3
|
89
|
+
|
90
|
+
|
91
|
+
# Adapted from
|
92
|
+
# https://github.com/google/gtest-parallel/blob/master/gtest_parallel.py#L37
|
93
|
+
#
|
94
|
+
# An object that catches SIGINT sent to the Python process and notices
|
95
|
+
# if processes passed to wait() die by SIGINT (we need to look for
|
96
|
+
# both of those cases, because pressing Ctrl+C can result in either
|
97
|
+
# the main process or one of the subprocesses getting the signal).
|
98
|
+
#
|
99
|
+
# Before a SIGINT is seen, wait(p) will simply call p.wait() and
|
100
|
+
# return the result. Once a SIGINT has been seen (in the main process
|
101
|
+
# or a subprocess, including the one the current call is waiting for),
|
102
|
+
# wait(p) will call p.terminate().
|
103
|
+
class SigintHandler(object):
|
104
|
+
sigint_returncodes = {-signal.SIGINT, # Unix
|
105
|
+
-1073741510, # Windows
|
106
|
+
}
|
107
|
+
def __init__(self):
|
108
|
+
self.__lock = threading.Lock()
|
109
|
+
self.__processes = set()
|
110
|
+
self.__got_sigint = False
|
111
|
+
self.__previous_signal = signal.signal(signal.SIGINT, self.interrupt)
|
112
|
+
|
113
|
+
def __on_sigint(self):
|
114
|
+
self.__got_sigint = True
|
115
|
+
while self.__processes:
|
116
|
+
try:
|
117
|
+
self.__processes.pop().terminate()
|
118
|
+
except OSError:
|
119
|
+
pass
|
120
|
+
|
121
|
+
def interrupt(self, signal_num, frame):
|
122
|
+
with self.__lock:
|
123
|
+
self.__on_sigint()
|
124
|
+
self.__previous_signal(signal_num, frame)
|
125
|
+
|
126
|
+
def got_sigint(self):
|
127
|
+
with self.__lock:
|
128
|
+
return self.__got_sigint
|
129
|
+
|
130
|
+
def wait(self, p, stdin):
|
131
|
+
with self.__lock:
|
132
|
+
if self.__got_sigint:
|
133
|
+
p.terminate()
|
134
|
+
self.__processes.add(p)
|
135
|
+
stdout, stderr = p.communicate(stdin)
|
136
|
+
code = p.returncode
|
137
|
+
with self.__lock:
|
138
|
+
self.__processes.discard(p)
|
139
|
+
if code in self.sigint_returncodes:
|
140
|
+
self.__on_sigint()
|
141
|
+
return stdout, stderr
|
142
|
+
|
143
|
+
sigint_handler = SigintHandler()
|
144
|
+
|
145
|
+
|
146
|
+
class Timer(object):
|
147
|
+
def __init__(self, timeout, fn):
|
148
|
+
self.completed = False
|
149
|
+
self._fn = fn
|
150
|
+
self._timer = threading.Timer(timeout, self._onTimer) if timeout else None
|
151
|
+
|
152
|
+
def __enter__(self):
|
153
|
+
if self._timer:
|
154
|
+
self._timer.start()
|
155
|
+
return self
|
156
|
+
|
157
|
+
def __exit__(self, _type, _value, _traceback):
|
158
|
+
if self._timer:
|
159
|
+
self._timer.cancel()
|
160
|
+
|
161
|
+
def _onTimer(self):
|
162
|
+
self._fn()
|
163
|
+
self.completed = True
|
164
|
+
|
165
|
+
|
166
|
+
class ThreadPool(object):
|
167
|
+
def __init__(self, pool_size=None, timeout=None):
|
168
|
+
self.timeout = timeout
|
169
|
+
self._pool_size = pool_size or multiprocessing.cpu_count()
|
170
|
+
self._messages = []
|
171
|
+
self._messages_lock = threading.Lock()
|
172
|
+
self._tests = []
|
173
|
+
self._tests_lock = threading.Lock()
|
174
|
+
self._nonparallel_tests = []
|
175
|
+
|
176
|
+
def _GetCommand(self, test):
|
177
|
+
vpython = 'vpython'
|
178
|
+
if test.python3:
|
179
|
+
vpython += '3'
|
180
|
+
if sys.platform == 'win32':
|
181
|
+
vpython += '.bat'
|
182
|
+
|
183
|
+
cmd = test.cmd
|
184
|
+
if cmd[0] == 'python':
|
185
|
+
cmd = list(cmd)
|
186
|
+
cmd[0] = vpython
|
187
|
+
elif cmd[0].endswith('.py'):
|
188
|
+
cmd = [vpython] + cmd
|
189
|
+
|
190
|
+
# On Windows, scripts on the current directory take precedence over PATH, so
|
191
|
+
# that when testing depot_tools on Windows, calling `vpython.bat` will
|
192
|
+
# execute the copy of vpython of the depot_tools under test instead of the
|
193
|
+
# one in the bot.
|
194
|
+
# As a workaround, we run the tests from the parent directory instead.
|
195
|
+
if (cmd[0] == vpython and
|
196
|
+
'cwd' in test.kwargs and
|
197
|
+
os.path.basename(test.kwargs['cwd']) == 'depot_tools'):
|
198
|
+
test.kwargs['cwd'] = os.path.dirname(test.kwargs['cwd'])
|
199
|
+
cmd[1] = os.path.join('depot_tools', cmd[1])
|
200
|
+
|
201
|
+
return cmd
|
202
|
+
|
203
|
+
def _RunWithTimeout(self, cmd, stdin, kwargs):
|
204
|
+
p = subprocess.Popen(cmd, **kwargs)
|
205
|
+
with Timer(self.timeout, p.terminate) as timer:
|
206
|
+
stdout, _ = sigint_handler.wait(p, stdin)
|
207
|
+
if timer.completed:
|
208
|
+
stdout = 'Process timed out after %ss\n%s' % (self.timeout, stdout)
|
209
|
+
return p.returncode, stdout
|
210
|
+
|
211
|
+
def CallCommand(self, test):
|
212
|
+
"""Runs an external program.
|
213
|
+
|
214
|
+
This function converts invocation of .py files and invocations of 'python'
|
215
|
+
to vpython invocations.
|
216
|
+
"""
|
217
|
+
cmd = self._GetCommand(test)
|
218
|
+
try:
|
219
|
+
start = time_time()
|
220
|
+
returncode, stdout = self._RunWithTimeout(cmd, test.stdin, test.kwargs)
|
221
|
+
duration = time_time() - start
|
222
|
+
except Exception:
|
223
|
+
duration = time_time() - start
|
224
|
+
return test.message(
|
225
|
+
'%s\n%s exec failure (%4.2fs)\n%s' % (
|
226
|
+
test.name, ' '.join(cmd), duration, traceback.format_exc()))
|
227
|
+
|
228
|
+
if returncode != 0:
|
229
|
+
return test.message(
|
230
|
+
'%s\n%s (%4.2fs) failed\n%s' % (
|
231
|
+
test.name, ' '.join(cmd), duration, stdout))
|
232
|
+
|
233
|
+
if test.info:
|
234
|
+
return test.info('%s\n%s (%4.2fs)' % (test.name, ' '.join(cmd), duration))
|
235
|
+
|
236
|
+
def AddTests(self, tests, parallel=True):
|
237
|
+
if parallel:
|
238
|
+
self._tests.extend(tests)
|
239
|
+
else:
|
240
|
+
self._nonparallel_tests.extend(tests)
|
241
|
+
|
242
|
+
def RunAsync(self):
|
243
|
+
self._messages = []
|
244
|
+
|
245
|
+
def _WorkerFn():
|
246
|
+
while True:
|
247
|
+
test = None
|
248
|
+
with self._tests_lock:
|
249
|
+
if not self._tests:
|
250
|
+
break
|
251
|
+
test = self._tests.pop()
|
252
|
+
result = self.CallCommand(test)
|
253
|
+
if result:
|
254
|
+
with self._messages_lock:
|
255
|
+
self._messages.append(result)
|
256
|
+
|
257
|
+
def _StartDaemon():
|
258
|
+
t = threading.Thread(target=_WorkerFn)
|
259
|
+
t.daemon = True
|
260
|
+
t.start()
|
261
|
+
return t
|
262
|
+
|
263
|
+
while self._nonparallel_tests:
|
264
|
+
test = self._nonparallel_tests.pop()
|
265
|
+
result = self.CallCommand(test)
|
266
|
+
if result:
|
267
|
+
self._messages.append(result)
|
268
|
+
|
269
|
+
if self._tests:
|
270
|
+
threads = [_StartDaemon() for _ in range(self._pool_size)]
|
271
|
+
for worker in threads:
|
272
|
+
worker.join()
|
273
|
+
|
274
|
+
return self._messages
|
275
|
+
|
276
|
+
|
277
|
+
def normpath(path):
|
278
|
+
'''Version of os.path.normpath that also changes backward slashes to
|
279
|
+
forward slashes when not running on Windows.
|
280
|
+
'''
|
281
|
+
# This is safe to always do because the Windows version of os.path.normpath
|
282
|
+
# will replace forward slashes with backward slashes.
|
283
|
+
path = path.replace(os.sep, '/')
|
284
|
+
return os.path.normpath(path)
|
285
|
+
|
286
|
+
|
287
|
+
def _RightHandSideLinesImpl(affected_files):
|
288
|
+
"""Implements RightHandSideLines for InputApi and GclChange."""
|
289
|
+
for af in affected_files:
|
290
|
+
lines = af.ChangedContents()
|
291
|
+
for line in lines:
|
292
|
+
yield (af, line[0], line[1])
|
293
|
+
|
294
|
+
|
295
|
+
def prompt_should_continue(prompt_string):
|
296
|
+
sys.stdout.write(prompt_string)
|
297
|
+
response = sys.stdin.readline().strip().lower()
|
298
|
+
return response in ('y', 'yes')
|
299
|
+
|
300
|
+
|
301
|
+
# Top level object so multiprocessing can pickle
|
302
|
+
# Public access through OutputApi object.
|
303
|
+
class _PresubmitResult(object):
|
304
|
+
"""Base class for result objects."""
|
305
|
+
fatal = False
|
306
|
+
should_prompt = False
|
307
|
+
|
308
|
+
def __init__(self, message, items=None, long_text=''):
|
309
|
+
"""
|
310
|
+
message: A short one-line message to indicate errors.
|
311
|
+
items: A list of short strings to indicate where errors occurred.
|
312
|
+
long_text: multi-line text output, e.g. from another tool
|
313
|
+
"""
|
314
|
+
self._message = message
|
315
|
+
self._items = items or []
|
316
|
+
self._long_text = long_text.rstrip()
|
317
|
+
|
318
|
+
def handle(self):
|
319
|
+
sys.stdout.write(self._message)
|
320
|
+
sys.stdout.write('\n')
|
321
|
+
for index, item in enumerate(self._items):
|
322
|
+
sys.stdout.write(' ')
|
323
|
+
# Write separately in case it's unicode.
|
324
|
+
sys.stdout.write(str(item))
|
325
|
+
if index < len(self._items) - 1:
|
326
|
+
sys.stdout.write(' \\')
|
327
|
+
sys.stdout.write('\n')
|
328
|
+
if self._long_text:
|
329
|
+
sys.stdout.write('\n***************\n')
|
330
|
+
# Write separately in case it's unicode.
|
331
|
+
sys.stdout.write(self._long_text)
|
332
|
+
sys.stdout.write('\n***************\n')
|
333
|
+
|
334
|
+
def json_format(self):
|
335
|
+
return {
|
336
|
+
'message': self._message,
|
337
|
+
'items': [str(item) for item in self._items],
|
338
|
+
'long_text': self._long_text,
|
339
|
+
'fatal': self.fatal
|
340
|
+
}
|
341
|
+
|
342
|
+
|
343
|
+
# Top level object so multiprocessing can pickle
|
344
|
+
# Public access through OutputApi object.
|
345
|
+
class _PresubmitError(_PresubmitResult):
|
346
|
+
"""A hard presubmit error."""
|
347
|
+
fatal = True
|
348
|
+
|
349
|
+
|
350
|
+
# Top level object so multiprocessing can pickle
|
351
|
+
# Public access through OutputApi object.
|
352
|
+
class _PresubmitPromptWarning(_PresubmitResult):
|
353
|
+
"""An warning that prompts the user if they want to continue."""
|
354
|
+
should_prompt = True
|
355
|
+
|
356
|
+
|
357
|
+
# Top level object so multiprocessing can pickle
|
358
|
+
# Public access through OutputApi object.
|
359
|
+
class _PresubmitNotifyResult(_PresubmitResult):
|
360
|
+
"""Just print something to the screen -- but it's not even a warning."""
|
361
|
+
pass
|
362
|
+
|
363
|
+
|
364
|
+
# Top level object so multiprocessing can pickle
|
365
|
+
# Public access through OutputApi object.
|
366
|
+
class _MailTextResult(_PresubmitResult):
|
367
|
+
"""A warning that should be included in the review request email."""
|
368
|
+
def __init__(self, *args, **kwargs):
|
369
|
+
super(_MailTextResult, self).__init__()
|
370
|
+
raise NotImplementedError()
|
371
|
+
|
372
|
+
class GerritAccessor(object):
|
373
|
+
"""Limited Gerrit functionality for canned presubmit checks to work.
|
374
|
+
|
375
|
+
To avoid excessive Gerrit calls, caches the results.
|
376
|
+
"""
|
377
|
+
|
378
|
+
def __init__(self, host):
|
379
|
+
self.host = host
|
380
|
+
self.cache = {}
|
381
|
+
|
382
|
+
def _FetchChangeDetail(self, issue):
|
383
|
+
# Separate function to be easily mocked in tests.
|
384
|
+
try:
|
385
|
+
return gerrit_util.GetChangeDetail(
|
386
|
+
self.host, str(issue),
|
387
|
+
['ALL_REVISIONS', 'DETAILED_LABELS', 'ALL_COMMITS'])
|
388
|
+
except gerrit_util.GerritError as e:
|
389
|
+
if e.http_status == 404:
|
390
|
+
raise Exception('Either Gerrit issue %s doesn\'t exist, or '
|
391
|
+
'no credentials to fetch issue details' % issue)
|
392
|
+
raise
|
393
|
+
|
394
|
+
def GetChangeInfo(self, issue):
|
395
|
+
"""Returns labels and all revisions (patchsets) for this issue.
|
396
|
+
|
397
|
+
The result is a dictionary according to Gerrit REST Api.
|
398
|
+
https://gerrit-review.googlesource.com/Documentation/rest-api.html
|
399
|
+
|
400
|
+
However, API isn't very clear what's inside, so see tests for example.
|
401
|
+
"""
|
402
|
+
assert issue
|
403
|
+
cache_key = int(issue)
|
404
|
+
if cache_key not in self.cache:
|
405
|
+
self.cache[cache_key] = self._FetchChangeDetail(issue)
|
406
|
+
return self.cache[cache_key]
|
407
|
+
|
408
|
+
def GetChangeDescription(self, issue, patchset=None):
|
409
|
+
"""If patchset is none, fetches current patchset."""
|
410
|
+
info = self.GetChangeInfo(issue)
|
411
|
+
# info is a reference to cache. We'll modify it here adding description to
|
412
|
+
# it to the right patchset, if it is not yet there.
|
413
|
+
|
414
|
+
# Find revision info for the patchset we want.
|
415
|
+
if patchset is not None:
|
416
|
+
for rev, rev_info in info['revisions'].items():
|
417
|
+
if str(rev_info['_number']) == str(patchset):
|
418
|
+
break
|
419
|
+
else:
|
420
|
+
raise Exception('patchset %s doesn\'t exist in issue %s' % (
|
421
|
+
patchset, issue))
|
422
|
+
else:
|
423
|
+
rev = info['current_revision']
|
424
|
+
rev_info = info['revisions'][rev]
|
425
|
+
|
426
|
+
return rev_info['commit']['message']
|
427
|
+
|
428
|
+
def GetDestRef(self, issue):
|
429
|
+
ref = self.GetChangeInfo(issue)['branch']
|
430
|
+
if not ref.startswith('refs/'):
|
431
|
+
# NOTE: it is possible to create 'refs/x' branch,
|
432
|
+
# aka 'refs/heads/refs/x'. However, this is ill-advised.
|
433
|
+
ref = 'refs/heads/%s' % ref
|
434
|
+
return ref
|
435
|
+
|
436
|
+
def GetChangeOwner(self, issue):
|
437
|
+
return self.GetChangeInfo(issue)['owner']['email']
|
438
|
+
|
439
|
+
def GetChangeReviewers(self, issue, approving_only=True):
|
440
|
+
changeinfo = self.GetChangeInfo(issue)
|
441
|
+
if approving_only:
|
442
|
+
labelinfo = changeinfo.get('labels', {}).get('Code-Review', {})
|
443
|
+
values = labelinfo.get('values', {}).keys()
|
444
|
+
try:
|
445
|
+
max_value = max(int(v) for v in values)
|
446
|
+
reviewers = [r for r in labelinfo.get('all', [])
|
447
|
+
if r.get('value', 0) == max_value]
|
448
|
+
except ValueError: # values is the empty list
|
449
|
+
reviewers = []
|
450
|
+
else:
|
451
|
+
reviewers = changeinfo.get('reviewers', {}).get('REVIEWER', [])
|
452
|
+
return [r.get('email') for r in reviewers]
|
453
|
+
|
454
|
+
def UpdateDescription(self, description, issue):
|
455
|
+
gerrit_util.SetCommitMessage(self.host, issue, description, notify='NONE')
|
456
|
+
|
457
|
+
|
458
|
+
class OutputApi(object):
|
459
|
+
"""An instance of OutputApi gets passed to presubmit scripts so that they
|
460
|
+
can output various types of results.
|
461
|
+
"""
|
462
|
+
PresubmitResult = _PresubmitResult
|
463
|
+
PresubmitError = _PresubmitError
|
464
|
+
PresubmitPromptWarning = _PresubmitPromptWarning
|
465
|
+
PresubmitNotifyResult = _PresubmitNotifyResult
|
466
|
+
MailTextResult = _MailTextResult
|
467
|
+
|
468
|
+
def __init__(self, is_committing):
|
469
|
+
self.is_committing = is_committing
|
470
|
+
self.more_cc = []
|
471
|
+
|
472
|
+
def AppendCC(self, cc):
|
473
|
+
"""Appends a user to cc for this change."""
|
474
|
+
self.more_cc.append(cc)
|
475
|
+
|
476
|
+
def PresubmitPromptOrNotify(self, *args, **kwargs):
|
477
|
+
"""Warn the user when uploading, but only notify if committing."""
|
478
|
+
if self.is_committing:
|
479
|
+
return self.PresubmitNotifyResult(*args, **kwargs)
|
480
|
+
return self.PresubmitPromptWarning(*args, **kwargs)
|
481
|
+
|
482
|
+
|
483
|
+
class InputApi(object):
|
484
|
+
"""An instance of this object is passed to presubmit scripts so they can
|
485
|
+
know stuff about the change they're looking at.
|
486
|
+
"""
|
487
|
+
# Method could be a function
|
488
|
+
# pylint: disable=no-self-use
|
489
|
+
|
490
|
+
# File extensions that are considered source files from a style guide
|
491
|
+
# perspective. Don't modify this list from a presubmit script!
|
492
|
+
#
|
493
|
+
# Files without an extension aren't included in the list. If you want to
|
494
|
+
# filter them as source files, add r'(^|.*?[\\\/])[^.]+$' to the allow list.
|
495
|
+
# Note that ALL CAPS files are skipped in DEFAULT_FILES_TO_SKIP below.
|
496
|
+
DEFAULT_FILES_TO_CHECK = (
|
497
|
+
# C++ and friends
|
498
|
+
r'.+\.c$', r'.+\.cc$', r'.+\.cpp$', r'.+\.h$', r'.+\.m$', r'.+\.mm$',
|
499
|
+
r'.+\.inl$', r'.+\.asm$', r'.+\.hxx$', r'.+\.hpp$', r'.+\.s$', r'.+\.S$',
|
500
|
+
# Scripts
|
501
|
+
r'.+\.js$', r'.+\.py$', r'.+\.sh$', r'.+\.rb$', r'.+\.pl$', r'.+\.pm$',
|
502
|
+
# Other
|
503
|
+
r'.+\.java$', r'.+\.mk$', r'.+\.am$', r'.+\.css$', r'.+\.mojom$',
|
504
|
+
r'.+\.fidl$'
|
505
|
+
)
|
506
|
+
|
507
|
+
# Path regexp that should be excluded from being considered containing source
|
508
|
+
# files. Don't modify this list from a presubmit script!
|
509
|
+
DEFAULT_FILES_TO_SKIP = (
|
510
|
+
r'testing_support[\\\/]google_appengine[\\\/].*',
|
511
|
+
r'.*\bexperimental[\\\/].*',
|
512
|
+
# Exclude third_party/.* but NOT third_party/{WebKit,blink}
|
513
|
+
# (crbug.com/539768 and crbug.com/836555).
|
514
|
+
r'.*\bthird_party[\\\/](?!(WebKit|blink)[\\\/]).*',
|
515
|
+
# Output directories (just in case)
|
516
|
+
r'.*\bDebug[\\\/].*',
|
517
|
+
r'.*\bRelease[\\\/].*',
|
518
|
+
r'.*\bxcodebuild[\\\/].*',
|
519
|
+
r'.*\bout[\\\/].*',
|
520
|
+
# All caps files like README and LICENCE.
|
521
|
+
r'.*\b[A-Z0-9_]{2,}$',
|
522
|
+
# SCM (can happen in dual SCM configuration). (Slightly over aggressive)
|
523
|
+
r'(|.*[\\\/])\.git[\\\/].*',
|
524
|
+
r'(|.*[\\\/])\.svn[\\\/].*',
|
525
|
+
# There is no point in processing a patch file.
|
526
|
+
r'.+\.diff$',
|
527
|
+
r'.+\.patch$',
|
528
|
+
)
|
529
|
+
|
530
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
531
|
+
@property
|
532
|
+
def DEFAULT_WHITE_LIST(self):
|
533
|
+
return self.DEFAULT_FILES_TO_CHECK
|
534
|
+
|
535
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
536
|
+
@DEFAULT_WHITE_LIST.setter
|
537
|
+
def DEFAULT_WHITE_LIST(self, value):
|
538
|
+
self.DEFAULT_FILES_TO_CHECK = value
|
539
|
+
|
540
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
541
|
+
@property
|
542
|
+
def DEFAULT_ALLOW_LIST(self):
|
543
|
+
return self.DEFAULT_FILES_TO_CHECK
|
544
|
+
|
545
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
546
|
+
@DEFAULT_ALLOW_LIST.setter
|
547
|
+
def DEFAULT_ALLOW_LIST(self, value):
|
548
|
+
self.DEFAULT_FILES_TO_CHECK = value
|
549
|
+
|
550
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
551
|
+
@property
|
552
|
+
def DEFAULT_BLACK_LIST(self):
|
553
|
+
return self.DEFAULT_FILES_TO_SKIP
|
554
|
+
|
555
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
556
|
+
@DEFAULT_BLACK_LIST.setter
|
557
|
+
def DEFAULT_BLACK_LIST(self, value):
|
558
|
+
self.DEFAULT_FILES_TO_SKIP = value
|
559
|
+
|
560
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
561
|
+
@property
|
562
|
+
def DEFAULT_BLOCK_LIST(self):
|
563
|
+
return self.DEFAULT_FILES_TO_SKIP
|
564
|
+
|
565
|
+
# TODO(https://crbug.com/1098562): Remove once no longer used
|
566
|
+
@DEFAULT_BLOCK_LIST.setter
|
567
|
+
def DEFAULT_BLOCK_LIST(self, value):
|
568
|
+
self.DEFAULT_FILES_TO_SKIP = value
|
569
|
+
|
570
|
+
def __init__(self, change, presubmit_path, is_committing,
|
571
|
+
verbose, gerrit_obj, dry_run=None, thread_pool=None, parallel=False):
|
572
|
+
"""Builds an InputApi object.
|
573
|
+
|
574
|
+
Args:
|
575
|
+
change: A presubmit.Change object.
|
576
|
+
presubmit_path: The path to the presubmit script being processed.
|
577
|
+
is_committing: True if the change is about to be committed.
|
578
|
+
gerrit_obj: provides basic Gerrit codereview functionality.
|
579
|
+
dry_run: if true, some Checks will be skipped.
|
580
|
+
parallel: if true, all tests reported via input_api.RunTests for all
|
581
|
+
PRESUBMIT files will be run in parallel.
|
582
|
+
"""
|
583
|
+
# Version number of the presubmit_support script.
|
584
|
+
self.version = [int(x) for x in __version__.split('.')]
|
585
|
+
self.change = change
|
586
|
+
self.is_committing = is_committing
|
587
|
+
self.gerrit = gerrit_obj
|
588
|
+
self.dry_run = dry_run
|
589
|
+
|
590
|
+
self.parallel = parallel
|
591
|
+
self.thread_pool = thread_pool or ThreadPool()
|
592
|
+
|
593
|
+
# We expose various modules and functions as attributes of the input_api
|
594
|
+
# so that presubmit scripts don't have to import them.
|
595
|
+
self.ast = ast
|
596
|
+
self.basename = os.path.basename
|
597
|
+
self.cpplint = cpplint
|
598
|
+
self.fnmatch = fnmatch
|
599
|
+
self.gclient_paths = gclient_paths
|
600
|
+
# TODO(yyanagisawa): stop exposing this when python3 become default.
|
601
|
+
# Since python3's tempfile has TemporaryDirectory, we do not need this.
|
602
|
+
self.temporary_directory = gclient_utils.temporary_directory
|
603
|
+
self.glob = glob.glob
|
604
|
+
self.json = json
|
605
|
+
self.logging = logging.getLogger('PRESUBMIT')
|
606
|
+
self.os_listdir = os.listdir
|
607
|
+
self.os_path = os.path
|
608
|
+
self.os_stat = os.stat
|
609
|
+
self.os_walk = os.walk
|
610
|
+
self.re = re
|
611
|
+
self.subprocess = subprocess
|
612
|
+
self.sys = sys
|
613
|
+
self.tempfile = tempfile
|
614
|
+
self.time = time
|
615
|
+
self.unittest = unittest
|
616
|
+
if sys.version_info.major == 2:
|
617
|
+
self.urllib2 = urllib2
|
618
|
+
self.urllib_request = urllib_request
|
619
|
+
self.urllib_error = urllib_error
|
620
|
+
|
621
|
+
self.is_windows = sys.platform == 'win32'
|
622
|
+
|
623
|
+
# Set python_executable to 'vpython' in order to allow scripts in other
|
624
|
+
# repos (e.g. src.git) to automatically pick up that repo's .vpython file,
|
625
|
+
# instead of inheriting the one in depot_tools.
|
626
|
+
self.python_executable = 'vpython'
|
627
|
+
self.environ = os.environ
|
628
|
+
|
629
|
+
# InputApi.platform is the platform you're currently running on.
|
630
|
+
self.platform = sys.platform
|
631
|
+
|
632
|
+
self.cpu_count = multiprocessing.cpu_count()
|
633
|
+
|
634
|
+
# The local path of the currently-being-processed presubmit script.
|
635
|
+
self._current_presubmit_path = os.path.dirname(presubmit_path)
|
636
|
+
|
637
|
+
# We carry the canned checks so presubmit scripts can easily use them.
|
638
|
+
self.canned_checks = presubmit_canned_checks
|
639
|
+
|
640
|
+
# Temporary files we must manually remove at the end of a run.
|
641
|
+
self._named_temporary_files = []
|
642
|
+
|
643
|
+
# TODO(dpranke): figure out a list of all approved owners for a repo
|
644
|
+
# in order to be able to handle wildcard OWNERS files?
|
645
|
+
self.owners_db = owners.Database(change.RepositoryRoot(),
|
646
|
+
fopen=open, os_path=self.os_path)
|
647
|
+
self.owners_finder = owners_finder.OwnersFinder
|
648
|
+
self.verbose = verbose
|
649
|
+
self.Command = CommandData
|
650
|
+
|
651
|
+
# Replace <hash_map> and <hash_set> as headers that need to be included
|
652
|
+
# with 'base/containers/hash_tables.h' instead.
|
653
|
+
# Access to a protected member _XX of a client class
|
654
|
+
# pylint: disable=protected-access
|
655
|
+
self.cpplint._re_pattern_templates = [
|
656
|
+
(a, b, 'base/containers/hash_tables.h')
|
657
|
+
if header in ('<hash_map>', '<hash_set>') else (a, b, header)
|
658
|
+
for (a, b, header) in cpplint._re_pattern_templates
|
659
|
+
]
|
660
|
+
|
661
|
+
def SetTimeout(self, timeout):
|
662
|
+
self.thread_pool.timeout = timeout
|
663
|
+
|
664
|
+
def PresubmitLocalPath(self):
|
665
|
+
"""Returns the local path of the presubmit script currently being run.
|
666
|
+
|
667
|
+
This is useful if you don't want to hard-code absolute paths in the
|
668
|
+
presubmit script. For example, It can be used to find another file
|
669
|
+
relative to the PRESUBMIT.py script, so the whole tree can be branched and
|
670
|
+
the presubmit script still works, without editing its content.
|
671
|
+
"""
|
672
|
+
return self._current_presubmit_path
|
673
|
+
|
674
|
+
def AffectedFiles(self, include_deletes=True, file_filter=None):
|
675
|
+
"""Same as input_api.change.AffectedFiles() except only lists files
|
676
|
+
(and optionally directories) in the same directory as the current presubmit
|
677
|
+
script, or subdirectories thereof. Note that files are listed using the OS
|
678
|
+
path separator, so backslashes are used as separators on Windows.
|
679
|
+
"""
|
680
|
+
dir_with_slash = normpath('%s/' % self.PresubmitLocalPath())
|
681
|
+
if len(dir_with_slash) == 1:
|
682
|
+
dir_with_slash = ''
|
683
|
+
|
684
|
+
return list(filter(
|
685
|
+
lambda x: normpath(x.AbsoluteLocalPath()).startswith(dir_with_slash),
|
686
|
+
self.change.AffectedFiles(include_deletes, file_filter)))
|
687
|
+
|
688
|
+
def LocalPaths(self):
|
689
|
+
"""Returns local paths of input_api.AffectedFiles()."""
|
690
|
+
paths = [af.LocalPath() for af in self.AffectedFiles()]
|
691
|
+
logging.debug('LocalPaths: %s', paths)
|
692
|
+
return paths
|
693
|
+
|
694
|
+
def AbsoluteLocalPaths(self):
|
695
|
+
"""Returns absolute local paths of input_api.AffectedFiles()."""
|
696
|
+
return [af.AbsoluteLocalPath() for af in self.AffectedFiles()]
|
697
|
+
|
698
|
+
def AffectedTestableFiles(self, include_deletes=None, **kwargs):
|
699
|
+
"""Same as input_api.change.AffectedTestableFiles() except only lists files
|
700
|
+
in the same directory as the current presubmit script, or subdirectories
|
701
|
+
thereof.
|
702
|
+
"""
|
703
|
+
if include_deletes is not None:
|
704
|
+
warn('AffectedTestableFiles(include_deletes=%s)'
|
705
|
+
' is deprecated and ignored' % str(include_deletes),
|
706
|
+
category=DeprecationWarning,
|
707
|
+
stacklevel=2)
|
708
|
+
return list(filter(
|
709
|
+
lambda x: x.IsTestableFile(),
|
710
|
+
self.AffectedFiles(include_deletes=False, **kwargs)))
|
711
|
+
|
712
|
+
def AffectedTextFiles(self, include_deletes=None):
|
713
|
+
"""An alias to AffectedTestableFiles for backwards compatibility."""
|
714
|
+
return self.AffectedTestableFiles(include_deletes=include_deletes)
|
715
|
+
|
716
|
+
def FilterSourceFile(self, affected_file, files_to_check=None,
|
717
|
+
files_to_skip=None, allow_list=None, block_list=None,
|
718
|
+
white_list=None, black_list=None):
|
719
|
+
"""Filters out files that aren't considered 'source file'.
|
720
|
+
|
721
|
+
If files_to_check or files_to_skip is None, InputApi.DEFAULT_FILES_TO_CHECK
|
722
|
+
and InputApi.DEFAULT_FILES_TO_SKIP is used respectively.
|
723
|
+
|
724
|
+
The lists will be compiled as regular expression and
|
725
|
+
AffectedFile.LocalPath() needs to pass both list.
|
726
|
+
|
727
|
+
Note: if files_to_check or files_to_skip is not set, and
|
728
|
+
white_list/allow_list or black_list/block_list is, then those values are
|
729
|
+
used. This is used for backward compatibility reasons.
|
730
|
+
|
731
|
+
Note: Copy-paste this function to suit your needs or use a lambda function.
|
732
|
+
"""
|
733
|
+
# TODO(https://crbug.com/1098560): Add warnings before removing bc.
|
734
|
+
if files_to_check is None:
|
735
|
+
files_to_check = allow_list or white_list
|
736
|
+
if files_to_skip is None:
|
737
|
+
files_to_skip = block_list or black_list
|
738
|
+
|
739
|
+
if files_to_check is None:
|
740
|
+
files_to_check = self.DEFAULT_FILES_TO_CHECK
|
741
|
+
if files_to_skip is None:
|
742
|
+
files_to_skip = self.DEFAULT_FILES_TO_SKIP
|
743
|
+
|
744
|
+
def Find(affected_file, items):
|
745
|
+
local_path = affected_file.LocalPath()
|
746
|
+
for item in items:
|
747
|
+
if self.re.match(item, local_path):
|
748
|
+
return True
|
749
|
+
return False
|
750
|
+
return (Find(affected_file, files_to_check) and
|
751
|
+
not Find(affected_file, files_to_skip))
|
752
|
+
|
753
|
+
def AffectedSourceFiles(self, source_file):
|
754
|
+
"""Filter the list of AffectedTestableFiles by the function source_file.
|
755
|
+
|
756
|
+
If source_file is None, InputApi.FilterSourceFile() is used.
|
757
|
+
"""
|
758
|
+
if not source_file:
|
759
|
+
source_file = self.FilterSourceFile
|
760
|
+
return list(filter(source_file, self.AffectedTestableFiles()))
|
761
|
+
|
762
|
+
def RightHandSideLines(self, source_file_filter=None):
|
763
|
+
"""An iterator over all text lines in 'new' version of changed files.
|
764
|
+
|
765
|
+
Only lists lines from new or modified text files in the change that are
|
766
|
+
contained by the directory of the currently executing presubmit script.
|
767
|
+
|
768
|
+
This is useful for doing line-by-line regex checks, like checking for
|
769
|
+
trailing whitespace.
|
770
|
+
|
771
|
+
Yields:
|
772
|
+
a 3 tuple:
|
773
|
+
the AffectedFile instance of the current file;
|
774
|
+
integer line number (1-based); and
|
775
|
+
the contents of the line as a string.
|
776
|
+
|
777
|
+
Note: The carriage return (LF or CR) is stripped off.
|
778
|
+
"""
|
779
|
+
files = self.AffectedSourceFiles(source_file_filter)
|
780
|
+
return _RightHandSideLinesImpl(files)
|
781
|
+
|
782
|
+
def ReadFile(self, file_item, mode='r'):
|
783
|
+
"""Reads an arbitrary file.
|
784
|
+
|
785
|
+
Deny reading anything outside the repository.
|
786
|
+
"""
|
787
|
+
if isinstance(file_item, AffectedFile):
|
788
|
+
file_item = file_item.AbsoluteLocalPath()
|
789
|
+
if not file_item.startswith(self.change.RepositoryRoot()):
|
790
|
+
raise IOError('Access outside the repository root is denied.')
|
791
|
+
return gclient_utils.FileRead(file_item, mode)
|
792
|
+
|
793
|
+
def CreateTemporaryFile(self, **kwargs):
|
794
|
+
"""Returns a named temporary file that must be removed with a call to
|
795
|
+
RemoveTemporaryFiles().
|
796
|
+
|
797
|
+
All keyword arguments are forwarded to tempfile.NamedTemporaryFile(),
|
798
|
+
except for |delete|, which is always set to False.
|
799
|
+
|
800
|
+
Presubmit checks that need to create a temporary file and pass it for
|
801
|
+
reading should use this function instead of NamedTemporaryFile(), as
|
802
|
+
Windows fails to open a file that is already open for writing.
|
803
|
+
|
804
|
+
with input_api.CreateTemporaryFile() as f:
|
805
|
+
f.write('xyz')
|
806
|
+
f.close()
|
807
|
+
input_api.subprocess.check_output(['script-that', '--reads-from',
|
808
|
+
f.name])
|
809
|
+
|
810
|
+
|
811
|
+
Note that callers of CreateTemporaryFile() should not worry about removing
|
812
|
+
any temporary file; this is done transparently by the presubmit handling
|
813
|
+
code.
|
814
|
+
"""
|
815
|
+
if 'delete' in kwargs:
|
816
|
+
# Prevent users from passing |delete|; we take care of file deletion
|
817
|
+
# ourselves and this prevents unintuitive error messages when we pass
|
818
|
+
# delete=False and 'delete' is also in kwargs.
|
819
|
+
raise TypeError('CreateTemporaryFile() does not take a "delete" '
|
820
|
+
'argument, file deletion is handled automatically by '
|
821
|
+
'the same presubmit_support code that creates InputApi '
|
822
|
+
'objects.')
|
823
|
+
temp_file = self.tempfile.NamedTemporaryFile(delete=False, **kwargs)
|
824
|
+
self._named_temporary_files.append(temp_file.name)
|
825
|
+
return temp_file
|
826
|
+
|
827
|
+
@property
|
828
|
+
def tbr(self):
|
829
|
+
"""Returns if a change is TBR'ed."""
|
830
|
+
return 'TBR' in self.change.tags or self.change.TBRsFromDescription()
|
831
|
+
|
832
|
+
def RunTests(self, tests_mix, parallel=True):
|
833
|
+
tests = []
|
834
|
+
msgs = []
|
835
|
+
for t in tests_mix:
|
836
|
+
if isinstance(t, OutputApi.PresubmitResult) and t:
|
837
|
+
msgs.append(t)
|
838
|
+
else:
|
839
|
+
assert issubclass(t.message, _PresubmitResult)
|
840
|
+
tests.append(t)
|
841
|
+
if self.verbose:
|
842
|
+
t.info = _PresubmitNotifyResult
|
843
|
+
if not t.kwargs.get('cwd'):
|
844
|
+
t.kwargs['cwd'] = self.PresubmitLocalPath()
|
845
|
+
self.thread_pool.AddTests(tests, parallel)
|
846
|
+
# When self.parallel is True (i.e. --parallel is passed as an option)
|
847
|
+
# RunTests doesn't actually run tests. It adds them to a ThreadPool that
|
848
|
+
# will run all tests once all PRESUBMIT files are processed.
|
849
|
+
# Otherwise, it will run them and return the results.
|
850
|
+
if not self.parallel:
|
851
|
+
msgs.extend(self.thread_pool.RunAsync())
|
852
|
+
return msgs
|
853
|
+
|
854
|
+
|
855
|
+
class _DiffCache(object):
|
856
|
+
"""Caches diffs retrieved from a particular SCM."""
|
857
|
+
def __init__(self, upstream=None):
|
858
|
+
"""Stores the upstream revision against which all diffs will be computed."""
|
859
|
+
self._upstream = upstream
|
860
|
+
|
861
|
+
def GetDiff(self, path, local_root):
|
862
|
+
"""Get the diff for a particular path."""
|
863
|
+
raise NotImplementedError()
|
864
|
+
|
865
|
+
def GetOldContents(self, path, local_root):
|
866
|
+
"""Get the old version for a particular path."""
|
867
|
+
raise NotImplementedError()
|
868
|
+
|
869
|
+
|
870
|
+
class _GitDiffCache(_DiffCache):
|
871
|
+
"""DiffCache implementation for git; gets all file diffs at once."""
|
872
|
+
def __init__(self, upstream):
|
873
|
+
super(_GitDiffCache, self).__init__(upstream=upstream)
|
874
|
+
self._diffs_by_file = None
|
875
|
+
|
876
|
+
def GetDiff(self, path, local_root):
|
877
|
+
if not self._diffs_by_file:
|
878
|
+
# Compute a single diff for all files and parse the output; should
|
879
|
+
# with git this is much faster than computing one diff for each file.
|
880
|
+
diffs = {}
|
881
|
+
|
882
|
+
# Don't specify any filenames below, because there are command line length
|
883
|
+
# limits on some platforms and GenerateDiff would fail.
|
884
|
+
unified_diff = scm.GIT.GenerateDiff(local_root, files=[], full_move=True,
|
885
|
+
branch=self._upstream)
|
886
|
+
|
887
|
+
# This regex matches the path twice, separated by a space. Note that
|
888
|
+
# filename itself may contain spaces.
|
889
|
+
file_marker = re.compile('^diff --git (?P<filename>.*) (?P=filename)$')
|
890
|
+
current_diff = []
|
891
|
+
keep_line_endings = True
|
892
|
+
for x in unified_diff.splitlines(keep_line_endings):
|
893
|
+
match = file_marker.match(x)
|
894
|
+
if match:
|
895
|
+
# Marks the start of a new per-file section.
|
896
|
+
diffs[match.group('filename')] = current_diff = [x]
|
897
|
+
elif x.startswith('diff --git'):
|
898
|
+
raise PresubmitFailure('Unexpected diff line: %s' % x)
|
899
|
+
else:
|
900
|
+
current_diff.append(x)
|
901
|
+
|
902
|
+
self._diffs_by_file = dict(
|
903
|
+
(normpath(path), ''.join(diff)) for path, diff in diffs.items())
|
904
|
+
|
905
|
+
if path not in self._diffs_by_file:
|
906
|
+
raise PresubmitFailure(
|
907
|
+
'Unified diff did not contain entry for file %s' % path)
|
908
|
+
|
909
|
+
return self._diffs_by_file[path]
|
910
|
+
|
911
|
+
def GetOldContents(self, path, local_root):
|
912
|
+
return scm.GIT.GetOldContents(local_root, path, branch=self._upstream)
|
913
|
+
|
914
|
+
|
915
|
+
class AffectedFile(object):
|
916
|
+
"""Representation of a file in a change."""
|
917
|
+
|
918
|
+
DIFF_CACHE = _DiffCache
|
919
|
+
|
920
|
+
# Method could be a function
|
921
|
+
# pylint: disable=no-self-use
|
922
|
+
def __init__(self, path, action, repository_root, diff_cache):
|
923
|
+
self._path = path
|
924
|
+
self._action = action
|
925
|
+
self._local_root = repository_root
|
926
|
+
self._is_directory = None
|
927
|
+
self._cached_changed_contents = None
|
928
|
+
self._cached_new_contents = None
|
929
|
+
self._diff_cache = diff_cache
|
930
|
+
logging.debug('%s(%s)', self.__class__.__name__, self._path)
|
931
|
+
|
932
|
+
def LocalPath(self):
|
933
|
+
"""Returns the path of this file on the local disk relative to client root.
|
934
|
+
|
935
|
+
This should be used for error messages but not for accessing files,
|
936
|
+
because presubmit checks are run with CWD=PresubmitLocalPath() (which is
|
937
|
+
often != client root).
|
938
|
+
"""
|
939
|
+
return normpath(self._path)
|
940
|
+
|
941
|
+
def AbsoluteLocalPath(self):
|
942
|
+
"""Returns the absolute path of this file on the local disk.
|
943
|
+
"""
|
944
|
+
return os.path.abspath(os.path.join(self._local_root, self.LocalPath()))
|
945
|
+
|
946
|
+
def Action(self):
|
947
|
+
"""Returns the action on this opened file, e.g. A, M, D, etc."""
|
948
|
+
return self._action
|
949
|
+
|
950
|
+
def IsTestableFile(self):
|
951
|
+
"""Returns True if the file is a text file and not a binary file.
|
952
|
+
|
953
|
+
Deleted files are not text file."""
|
954
|
+
raise NotImplementedError() # Implement when needed
|
955
|
+
|
956
|
+
def IsTextFile(self):
|
957
|
+
"""An alias to IsTestableFile for backwards compatibility."""
|
958
|
+
return self.IsTestableFile()
|
959
|
+
|
960
|
+
def OldContents(self):
|
961
|
+
"""Returns an iterator over the lines in the old version of file.
|
962
|
+
|
963
|
+
The old version is the file before any modifications in the user's
|
964
|
+
workspace, i.e. the 'left hand side'.
|
965
|
+
|
966
|
+
Contents will be empty if the file is a directory or does not exist.
|
967
|
+
Note: The carriage returns (LF or CR) are stripped off.
|
968
|
+
"""
|
969
|
+
return self._diff_cache.GetOldContents(self.LocalPath(),
|
970
|
+
self._local_root).splitlines()
|
971
|
+
|
972
|
+
def NewContents(self):
|
973
|
+
"""Returns an iterator over the lines in the new version of file.
|
974
|
+
|
975
|
+
The new version is the file in the user's workspace, i.e. the 'right hand
|
976
|
+
side'.
|
977
|
+
|
978
|
+
Contents will be empty if the file is a directory or does not exist.
|
979
|
+
Note: The carriage returns (LF or CR) are stripped off.
|
980
|
+
"""
|
981
|
+
if self._cached_new_contents is None:
|
982
|
+
self._cached_new_contents = []
|
983
|
+
try:
|
984
|
+
self._cached_new_contents = gclient_utils.FileRead(
|
985
|
+
self.AbsoluteLocalPath(), 'rU').splitlines()
|
986
|
+
except IOError:
|
987
|
+
pass # File not found? That's fine; maybe it was deleted.
|
988
|
+
return self._cached_new_contents[:]
|
989
|
+
|
990
|
+
def ChangedContents(self):
|
991
|
+
"""Returns a list of tuples (line number, line text) of all new lines.
|
992
|
+
|
993
|
+
This relies on the scm diff output describing each changed code section
|
994
|
+
with a line of the form
|
995
|
+
|
996
|
+
^@@ <old line num>,<old size> <new line num>,<new size> @@$
|
997
|
+
"""
|
998
|
+
if self._cached_changed_contents is not None:
|
999
|
+
return self._cached_changed_contents[:]
|
1000
|
+
self._cached_changed_contents = []
|
1001
|
+
line_num = 0
|
1002
|
+
|
1003
|
+
for line in self.GenerateScmDiff().splitlines():
|
1004
|
+
m = re.match(r'^@@ [0-9\,\+\-]+ \+([0-9]+)\,[0-9]+ @@', line)
|
1005
|
+
if m:
|
1006
|
+
line_num = int(m.groups(1)[0])
|
1007
|
+
continue
|
1008
|
+
if line.startswith('+') and not line.startswith('++'):
|
1009
|
+
self._cached_changed_contents.append((line_num, line[1:]))
|
1010
|
+
if not line.startswith('-'):
|
1011
|
+
line_num += 1
|
1012
|
+
return self._cached_changed_contents[:]
|
1013
|
+
|
1014
|
+
def __str__(self):
|
1015
|
+
return self.LocalPath()
|
1016
|
+
|
1017
|
+
def GenerateScmDiff(self):
|
1018
|
+
return self._diff_cache.GetDiff(self.LocalPath(), self._local_root)
|
1019
|
+
|
1020
|
+
|
1021
|
+
class GitAffectedFile(AffectedFile):
|
1022
|
+
"""Representation of a file in a change out of a git checkout."""
|
1023
|
+
# Method 'NNN' is abstract in class 'NNN' but is not overridden
|
1024
|
+
# pylint: disable=abstract-method
|
1025
|
+
|
1026
|
+
DIFF_CACHE = _GitDiffCache
|
1027
|
+
|
1028
|
+
def __init__(self, *args, **kwargs):
|
1029
|
+
AffectedFile.__init__(self, *args, **kwargs)
|
1030
|
+
self._server_path = None
|
1031
|
+
self._is_testable_file = None
|
1032
|
+
|
1033
|
+
def IsTestableFile(self):
|
1034
|
+
if self._is_testable_file is None:
|
1035
|
+
if self.Action() == 'D':
|
1036
|
+
# A deleted file is not testable.
|
1037
|
+
self._is_testable_file = False
|
1038
|
+
else:
|
1039
|
+
self._is_testable_file = os.path.isfile(self.AbsoluteLocalPath())
|
1040
|
+
return self._is_testable_file
|
1041
|
+
|
1042
|
+
|
1043
|
+
class Change(object):
|
1044
|
+
"""Describe a change.
|
1045
|
+
|
1046
|
+
Used directly by the presubmit scripts to query the current change being
|
1047
|
+
tested.
|
1048
|
+
|
1049
|
+
Instance members:
|
1050
|
+
tags: Dictionary of KEY=VALUE pairs found in the change description.
|
1051
|
+
self.KEY: equivalent to tags['KEY']
|
1052
|
+
"""
|
1053
|
+
|
1054
|
+
_AFFECTED_FILES = AffectedFile
|
1055
|
+
|
1056
|
+
# Matches key/value (or 'tag') lines in changelist descriptions.
|
1057
|
+
TAG_LINE_RE = re.compile(
|
1058
|
+
'^[ \t]*(?P<key>[A-Z][A-Z_0-9]*)[ \t]*=[ \t]*(?P<value>.*?)[ \t]*$')
|
1059
|
+
scm = ''
|
1060
|
+
|
1061
|
+
def __init__(
|
1062
|
+
self, name, description, local_root, files, issue, patchset, author,
|
1063
|
+
upstream=None):
|
1064
|
+
if files is None:
|
1065
|
+
files = []
|
1066
|
+
self._name = name
|
1067
|
+
# Convert root into an absolute path.
|
1068
|
+
self._local_root = os.path.abspath(local_root)
|
1069
|
+
self._upstream = upstream
|
1070
|
+
self.issue = issue
|
1071
|
+
self.patchset = patchset
|
1072
|
+
self.author_email = author
|
1073
|
+
|
1074
|
+
self._full_description = ''
|
1075
|
+
self.tags = {}
|
1076
|
+
self._description_without_tags = ''
|
1077
|
+
self.SetDescriptionText(description)
|
1078
|
+
|
1079
|
+
assert all(
|
1080
|
+
(isinstance(f, (list, tuple)) and len(f) == 2) for f in files), files
|
1081
|
+
|
1082
|
+
diff_cache = self._AFFECTED_FILES.DIFF_CACHE(self._upstream)
|
1083
|
+
self._affected_files = [
|
1084
|
+
self._AFFECTED_FILES(path, action.strip(), self._local_root, diff_cache)
|
1085
|
+
for action, path in files
|
1086
|
+
]
|
1087
|
+
|
1088
|
+
def Name(self):
|
1089
|
+
"""Returns the change name."""
|
1090
|
+
return self._name
|
1091
|
+
|
1092
|
+
def DescriptionText(self):
|
1093
|
+
"""Returns the user-entered changelist description, minus tags.
|
1094
|
+
|
1095
|
+
Any line in the user-provided description starting with e.g. 'FOO='
|
1096
|
+
(whitespace permitted before and around) is considered a tag line. Such
|
1097
|
+
lines are stripped out of the description this function returns.
|
1098
|
+
"""
|
1099
|
+
return self._description_without_tags
|
1100
|
+
|
1101
|
+
def FullDescriptionText(self):
|
1102
|
+
"""Returns the complete changelist description including tags."""
|
1103
|
+
return self._full_description
|
1104
|
+
|
1105
|
+
def SetDescriptionText(self, description):
|
1106
|
+
"""Sets the full description text (including tags) to |description|.
|
1107
|
+
|
1108
|
+
Also updates the list of tags."""
|
1109
|
+
self._full_description = description
|
1110
|
+
|
1111
|
+
# From the description text, build up a dictionary of key/value pairs
|
1112
|
+
# plus the description minus all key/value or 'tag' lines.
|
1113
|
+
description_without_tags = []
|
1114
|
+
self.tags = {}
|
1115
|
+
for line in self._full_description.splitlines():
|
1116
|
+
m = self.TAG_LINE_RE.match(line)
|
1117
|
+
if m:
|
1118
|
+
self.tags[m.group('key')] = m.group('value')
|
1119
|
+
else:
|
1120
|
+
description_without_tags.append(line)
|
1121
|
+
|
1122
|
+
# Change back to text and remove whitespace at end.
|
1123
|
+
self._description_without_tags = (
|
1124
|
+
'\n'.join(description_without_tags).rstrip())
|
1125
|
+
|
1126
|
+
def AddDescriptionFooter(self, key, value):
|
1127
|
+
"""Adds the given footer to the change description.
|
1128
|
+
|
1129
|
+
Args:
|
1130
|
+
key: A string with the key for the git footer. It must conform to
|
1131
|
+
the git footers format (i.e. 'List-Of-Tokens') and will be case
|
1132
|
+
normalized so that each token is title-cased.
|
1133
|
+
value: A string with the value for the git footer.
|
1134
|
+
"""
|
1135
|
+
description = git_footers.add_footer(
|
1136
|
+
self.FullDescriptionText(), git_footers.normalize_name(key), value)
|
1137
|
+
self.SetDescriptionText(description)
|
1138
|
+
|
1139
|
+
def RepositoryRoot(self):
|
1140
|
+
"""Returns the repository (checkout) root directory for this change,
|
1141
|
+
as an absolute path.
|
1142
|
+
"""
|
1143
|
+
return self._local_root
|
1144
|
+
|
1145
|
+
def __getattr__(self, attr):
|
1146
|
+
"""Return tags directly as attributes on the object."""
|
1147
|
+
if not re.match(r'^[A-Z_]*$', attr):
|
1148
|
+
raise AttributeError(self, attr)
|
1149
|
+
return self.tags.get(attr)
|
1150
|
+
|
1151
|
+
def GitFootersFromDescription(self):
|
1152
|
+
"""Return the git footers present in the description.
|
1153
|
+
|
1154
|
+
Returns:
|
1155
|
+
footers: A dict of {footer: [values]} containing a multimap of the footers
|
1156
|
+
in the change description.
|
1157
|
+
"""
|
1158
|
+
return git_footers.parse_footers(self.FullDescriptionText())
|
1159
|
+
|
1160
|
+
def BugsFromDescription(self):
|
1161
|
+
"""Returns all bugs referenced in the commit description."""
|
1162
|
+
tags = [b.strip() for b in self.tags.get('BUG', '').split(',') if b.strip()]
|
1163
|
+
footers = []
|
1164
|
+
parsed = self.GitFootersFromDescription()
|
1165
|
+
unsplit_footers = parsed.get('Bug', []) + parsed.get('Fixed', [])
|
1166
|
+
for unsplit_footer in unsplit_footers:
|
1167
|
+
footers += [b.strip() for b in unsplit_footer.split(',')]
|
1168
|
+
return sorted(set(tags + footers))
|
1169
|
+
|
1170
|
+
def ReviewersFromDescription(self):
|
1171
|
+
"""Returns all reviewers listed in the commit description."""
|
1172
|
+
# We don't support a 'R:' git-footer for reviewers; that is in metadata.
|
1173
|
+
tags = [r.strip() for r in self.tags.get('R', '').split(',') if r.strip()]
|
1174
|
+
return sorted(set(tags))
|
1175
|
+
|
1176
|
+
def TBRsFromDescription(self):
|
1177
|
+
"""Returns all TBR reviewers listed in the commit description."""
|
1178
|
+
tags = [r.strip() for r in self.tags.get('TBR', '').split(',') if r.strip()]
|
1179
|
+
# TODO(crbug.com/839208): Remove support for 'Tbr:' when TBRs are
|
1180
|
+
# programmatically determined by self-CR+1s.
|
1181
|
+
footers = self.GitFootersFromDescription().get('Tbr', [])
|
1182
|
+
return sorted(set(tags + footers))
|
1183
|
+
|
1184
|
+
# TODO(crbug.com/753425): Delete these once we're sure they're unused.
|
1185
|
+
@property
|
1186
|
+
def BUG(self):
|
1187
|
+
return ','.join(self.BugsFromDescription())
|
1188
|
+
@property
|
1189
|
+
def R(self):
|
1190
|
+
return ','.join(self.ReviewersFromDescription())
|
1191
|
+
@property
|
1192
|
+
def TBR(self):
|
1193
|
+
return ','.join(self.TBRsFromDescription())
|
1194
|
+
|
1195
|
+
def AllFiles(self, root=None):
|
1196
|
+
"""List all files under source control in the repo."""
|
1197
|
+
raise NotImplementedError()
|
1198
|
+
|
1199
|
+
def AffectedFiles(self, include_deletes=True, file_filter=None):
|
1200
|
+
"""Returns a list of AffectedFile instances for all files in the change.
|
1201
|
+
|
1202
|
+
Args:
|
1203
|
+
include_deletes: If false, deleted files will be filtered out.
|
1204
|
+
file_filter: An additional filter to apply.
|
1205
|
+
|
1206
|
+
Returns:
|
1207
|
+
[AffectedFile(path, action), AffectedFile(path, action)]
|
1208
|
+
"""
|
1209
|
+
affected = list(filter(file_filter, self._affected_files))
|
1210
|
+
|
1211
|
+
if include_deletes:
|
1212
|
+
return affected
|
1213
|
+
return list(filter(lambda x: x.Action() != 'D', affected))
|
1214
|
+
|
1215
|
+
def AffectedTestableFiles(self, include_deletes=None, **kwargs):
|
1216
|
+
"""Return a list of the existing text files in a change."""
|
1217
|
+
if include_deletes is not None:
|
1218
|
+
warn('AffectedTeestableFiles(include_deletes=%s)'
|
1219
|
+
' is deprecated and ignored' % str(include_deletes),
|
1220
|
+
category=DeprecationWarning,
|
1221
|
+
stacklevel=2)
|
1222
|
+
return list(filter(
|
1223
|
+
lambda x: x.IsTestableFile(),
|
1224
|
+
self.AffectedFiles(include_deletes=False, **kwargs)))
|
1225
|
+
|
1226
|
+
def AffectedTextFiles(self, include_deletes=None):
|
1227
|
+
"""An alias to AffectedTestableFiles for backwards compatibility."""
|
1228
|
+
return self.AffectedTestableFiles(include_deletes=include_deletes)
|
1229
|
+
|
1230
|
+
def LocalPaths(self):
|
1231
|
+
"""Convenience function."""
|
1232
|
+
return [af.LocalPath() for af in self.AffectedFiles()]
|
1233
|
+
|
1234
|
+
def AbsoluteLocalPaths(self):
|
1235
|
+
"""Convenience function."""
|
1236
|
+
return [af.AbsoluteLocalPath() for af in self.AffectedFiles()]
|
1237
|
+
|
1238
|
+
def RightHandSideLines(self):
|
1239
|
+
"""An iterator over all text lines in 'new' version of changed files.
|
1240
|
+
|
1241
|
+
Lists lines from new or modified text files in the change.
|
1242
|
+
|
1243
|
+
This is useful for doing line-by-line regex checks, like checking for
|
1244
|
+
trailing whitespace.
|
1245
|
+
|
1246
|
+
Yields:
|
1247
|
+
a 3 tuple:
|
1248
|
+
the AffectedFile instance of the current file;
|
1249
|
+
integer line number (1-based); and
|
1250
|
+
the contents of the line as a string.
|
1251
|
+
"""
|
1252
|
+
return _RightHandSideLinesImpl(
|
1253
|
+
x for x in self.AffectedFiles(include_deletes=False)
|
1254
|
+
if x.IsTestableFile())
|
1255
|
+
|
1256
|
+
def OriginalOwnersFiles(self):
|
1257
|
+
"""A map from path names of affected OWNERS files to their old content."""
|
1258
|
+
def owners_file_filter(f):
|
1259
|
+
return 'OWNERS' in os.path.split(f.LocalPath())[1]
|
1260
|
+
files = self.AffectedFiles(file_filter=owners_file_filter)
|
1261
|
+
return dict([(f.LocalPath(), f.OldContents()) for f in files])
|
1262
|
+
|
1263
|
+
|
1264
|
+
class GitChange(Change):
|
1265
|
+
_AFFECTED_FILES = GitAffectedFile
|
1266
|
+
scm = 'git'
|
1267
|
+
|
1268
|
+
def AllFiles(self, root=None):
|
1269
|
+
"""List all files under source control in the repo."""
|
1270
|
+
root = root or self.RepositoryRoot()
|
1271
|
+
return subprocess.check_output(
|
1272
|
+
['git', '-c', 'core.quotePath=false', 'ls-files', '--', '.'],
|
1273
|
+
cwd=root).splitlines()
|
1274
|
+
|
1275
|
+
|
1276
|
+
def ListRelevantPresubmitFiles(files, root):
|
1277
|
+
"""Finds all presubmit files that apply to a given set of source files.
|
1278
|
+
|
1279
|
+
If inherit-review-settings-ok is present right under root, looks for
|
1280
|
+
PRESUBMIT.py in directories enclosing root.
|
1281
|
+
|
1282
|
+
Args:
|
1283
|
+
files: An iterable container containing file paths.
|
1284
|
+
root: Path where to stop searching.
|
1285
|
+
|
1286
|
+
Return:
|
1287
|
+
List of absolute paths of the existing PRESUBMIT.py scripts.
|
1288
|
+
"""
|
1289
|
+
files = [normpath(os.path.join(root, f)) for f in files]
|
1290
|
+
|
1291
|
+
# List all the individual directories containing files.
|
1292
|
+
directories = set([os.path.dirname(f) for f in files])
|
1293
|
+
|
1294
|
+
# Ignore root if inherit-review-settings-ok is present.
|
1295
|
+
if os.path.isfile(os.path.join(root, 'inherit-review-settings-ok')):
|
1296
|
+
root = None
|
1297
|
+
|
1298
|
+
# Collect all unique directories that may contain PRESUBMIT.py.
|
1299
|
+
candidates = set()
|
1300
|
+
for directory in directories:
|
1301
|
+
while True:
|
1302
|
+
if directory in candidates:
|
1303
|
+
break
|
1304
|
+
candidates.add(directory)
|
1305
|
+
if directory == root:
|
1306
|
+
break
|
1307
|
+
parent_dir = os.path.dirname(directory)
|
1308
|
+
if parent_dir == directory:
|
1309
|
+
# We hit the system root directory.
|
1310
|
+
break
|
1311
|
+
directory = parent_dir
|
1312
|
+
|
1313
|
+
# Look for PRESUBMIT.py in all candidate directories.
|
1314
|
+
results = []
|
1315
|
+
for directory in sorted(list(candidates)):
|
1316
|
+
try:
|
1317
|
+
for f in os.listdir(directory):
|
1318
|
+
p = os.path.join(directory, f)
|
1319
|
+
if os.path.isfile(p) and re.match(
|
1320
|
+
r'PRESUBMIT.*\.py$', f) and not f.startswith('PRESUBMIT_test'):
|
1321
|
+
results.append(p)
|
1322
|
+
except OSError:
|
1323
|
+
pass
|
1324
|
+
|
1325
|
+
logging.debug('Presubmit files: %s', ','.join(results))
|
1326
|
+
return results
|
1327
|
+
|
1328
|
+
|
1329
|
+
class GetTryMastersExecuter(object):
|
1330
|
+
@staticmethod
|
1331
|
+
def ExecPresubmitScript(script_text, presubmit_path, project, change):
|
1332
|
+
"""Executes GetPreferredTryMasters() from a single presubmit script.
|
1333
|
+
|
1334
|
+
Args:
|
1335
|
+
script_text: The text of the presubmit script.
|
1336
|
+
presubmit_path: Project script to run.
|
1337
|
+
project: Project name to pass to presubmit script for bot selection.
|
1338
|
+
|
1339
|
+
Return:
|
1340
|
+
A map of try masters to map of builders to set of tests.
|
1341
|
+
"""
|
1342
|
+
context = {}
|
1343
|
+
try:
|
1344
|
+
exec(compile(script_text, 'PRESUBMIT.py', 'exec', dont_inherit=True),
|
1345
|
+
context)
|
1346
|
+
except Exception as e:
|
1347
|
+
raise PresubmitFailure('"%s" had an exception.\n%s'
|
1348
|
+
% (presubmit_path, e))
|
1349
|
+
|
1350
|
+
function_name = 'GetPreferredTryMasters'
|
1351
|
+
if function_name not in context:
|
1352
|
+
return {}
|
1353
|
+
get_preferred_try_masters = context[function_name]
|
1354
|
+
if not len(inspect.getargspec(get_preferred_try_masters)[0]) == 2:
|
1355
|
+
raise PresubmitFailure(
|
1356
|
+
'Expected function "GetPreferredTryMasters" to take two arguments.')
|
1357
|
+
return get_preferred_try_masters(project, change)
|
1358
|
+
|
1359
|
+
|
1360
|
+
class GetPostUploadExecuter(object):
|
1361
|
+
@staticmethod
|
1362
|
+
def ExecPresubmitScript(script_text, presubmit_path, gerrit_obj, change):
|
1363
|
+
"""Executes PostUploadHook() from a single presubmit script.
|
1364
|
+
|
1365
|
+
Args:
|
1366
|
+
script_text: The text of the presubmit script.
|
1367
|
+
presubmit_path: Project script to run.
|
1368
|
+
gerrit_obj: The GerritAccessor object.
|
1369
|
+
change: The Change object.
|
1370
|
+
|
1371
|
+
Return:
|
1372
|
+
A list of results objects.
|
1373
|
+
"""
|
1374
|
+
context = {}
|
1375
|
+
try:
|
1376
|
+
exec(compile(script_text, 'PRESUBMIT.py', 'exec', dont_inherit=True),
|
1377
|
+
context)
|
1378
|
+
except Exception as e:
|
1379
|
+
raise PresubmitFailure('"%s" had an exception.\n%s'
|
1380
|
+
% (presubmit_path, e))
|
1381
|
+
|
1382
|
+
function_name = 'PostUploadHook'
|
1383
|
+
if function_name not in context:
|
1384
|
+
return {}
|
1385
|
+
post_upload_hook = context[function_name]
|
1386
|
+
if not len(inspect.getargspec(post_upload_hook)[0]) == 3:
|
1387
|
+
raise PresubmitFailure(
|
1388
|
+
'Expected function "PostUploadHook" to take three arguments.')
|
1389
|
+
return post_upload_hook(gerrit_obj, change, OutputApi(False))
|
1390
|
+
|
1391
|
+
|
1392
|
+
def _MergeMasters(masters1, masters2):
|
1393
|
+
"""Merges two master maps. Merges also the tests of each builder."""
|
1394
|
+
result = {}
|
1395
|
+
for (master, builders) in itertools.chain(masters1.items(),
|
1396
|
+
masters2.items()):
|
1397
|
+
new_builders = result.setdefault(master, {})
|
1398
|
+
for (builder, tests) in builders.items():
|
1399
|
+
new_builders.setdefault(builder, set([])).update(tests)
|
1400
|
+
return result
|
1401
|
+
|
1402
|
+
|
1403
|
+
def DoGetTryMasters(change,
|
1404
|
+
changed_files,
|
1405
|
+
repository_root,
|
1406
|
+
default_presubmit,
|
1407
|
+
project,
|
1408
|
+
verbose,
|
1409
|
+
output_stream):
|
1410
|
+
"""Get the list of try masters from the presubmit scripts.
|
1411
|
+
|
1412
|
+
Args:
|
1413
|
+
changed_files: List of modified files.
|
1414
|
+
repository_root: The repository root.
|
1415
|
+
default_presubmit: A default presubmit script to execute in any case.
|
1416
|
+
project: Optional name of a project used in selecting trybots.
|
1417
|
+
verbose: Prints debug info.
|
1418
|
+
output_stream: A stream to write debug output to.
|
1419
|
+
|
1420
|
+
Return:
|
1421
|
+
Map of try masters to map of builders to set of tests.
|
1422
|
+
"""
|
1423
|
+
presubmit_files = ListRelevantPresubmitFiles(changed_files, repository_root)
|
1424
|
+
if not presubmit_files and verbose:
|
1425
|
+
output_stream.write('Warning, no PRESUBMIT.py found.\n')
|
1426
|
+
results = {}
|
1427
|
+
executer = GetTryMastersExecuter()
|
1428
|
+
|
1429
|
+
if default_presubmit:
|
1430
|
+
if verbose:
|
1431
|
+
output_stream.write('Running default presubmit script.\n')
|
1432
|
+
fake_path = os.path.join(repository_root, 'PRESUBMIT.py')
|
1433
|
+
results = _MergeMasters(results, executer.ExecPresubmitScript(
|
1434
|
+
default_presubmit, fake_path, project, change))
|
1435
|
+
for filename in presubmit_files:
|
1436
|
+
filename = os.path.abspath(filename)
|
1437
|
+
if verbose:
|
1438
|
+
output_stream.write('Running %s\n' % filename)
|
1439
|
+
# Accept CRLF presubmit script.
|
1440
|
+
presubmit_script = gclient_utils.FileRead(filename, 'rU')
|
1441
|
+
results = _MergeMasters(results, executer.ExecPresubmitScript(
|
1442
|
+
presubmit_script, filename, project, change))
|
1443
|
+
|
1444
|
+
# Make sets to lists again for later JSON serialization.
|
1445
|
+
for builders in results.values():
|
1446
|
+
for builder in builders:
|
1447
|
+
builders[builder] = list(builders[builder])
|
1448
|
+
|
1449
|
+
if results and verbose:
|
1450
|
+
output_stream.write('%s\n' % str(results))
|
1451
|
+
return results
|
1452
|
+
|
1453
|
+
|
1454
|
+
def DoPostUploadExecuter(change,
|
1455
|
+
gerrit_obj,
|
1456
|
+
verbose):
|
1457
|
+
"""Execute the post upload hook.
|
1458
|
+
|
1459
|
+
Args:
|
1460
|
+
change: The Change object.
|
1461
|
+
gerrit_obj: The GerritAccessor object.
|
1462
|
+
verbose: Prints debug info.
|
1463
|
+
"""
|
1464
|
+
presubmit_files = ListRelevantPresubmitFiles(
|
1465
|
+
change.LocalPaths(), change.RepositoryRoot())
|
1466
|
+
if not presubmit_files and verbose:
|
1467
|
+
sys.stdout.write('Warning, no PRESUBMIT.py found.\n')
|
1468
|
+
results = []
|
1469
|
+
executer = GetPostUploadExecuter()
|
1470
|
+
# The root presubmit file should be executed after the ones in subdirectories.
|
1471
|
+
# i.e. the specific post upload hooks should run before the general ones.
|
1472
|
+
# Thus, reverse the order provided by ListRelevantPresubmitFiles.
|
1473
|
+
presubmit_files.reverse()
|
1474
|
+
|
1475
|
+
for filename in presubmit_files:
|
1476
|
+
filename = os.path.abspath(filename)
|
1477
|
+
if verbose:
|
1478
|
+
sys.stdout.write('Running %s\n' % filename)
|
1479
|
+
# Accept CRLF presubmit script.
|
1480
|
+
presubmit_script = gclient_utils.FileRead(filename, 'rU')
|
1481
|
+
results.extend(executer.ExecPresubmitScript(
|
1482
|
+
presubmit_script, filename, gerrit_obj, change))
|
1483
|
+
|
1484
|
+
if not results:
|
1485
|
+
return 0
|
1486
|
+
|
1487
|
+
sys.stdout.write('\n')
|
1488
|
+
sys.stdout.write('** Post Upload Hook Messages **\n')
|
1489
|
+
|
1490
|
+
exit_code = 0
|
1491
|
+
for result in results:
|
1492
|
+
if result.fatal:
|
1493
|
+
exit_code = 1
|
1494
|
+
result.handle()
|
1495
|
+
sys.stdout.write('\n')
|
1496
|
+
|
1497
|
+
return exit_code
|
1498
|
+
|
1499
|
+
|
1500
|
+
class PresubmitExecuter(object):
|
1501
|
+
def __init__(self, change, committing, verbose,
|
1502
|
+
gerrit_obj, dry_run=None, thread_pool=None, parallel=False):
|
1503
|
+
"""
|
1504
|
+
Args:
|
1505
|
+
change: The Change object.
|
1506
|
+
committing: True if 'git cl land' is running, False if 'git cl upload' is.
|
1507
|
+
gerrit_obj: provides basic Gerrit codereview functionality.
|
1508
|
+
dry_run: if true, some Checks will be skipped.
|
1509
|
+
parallel: if true, all tests reported via input_api.RunTests for all
|
1510
|
+
PRESUBMIT files will be run in parallel.
|
1511
|
+
"""
|
1512
|
+
self.change = change
|
1513
|
+
self.committing = committing
|
1514
|
+
self.gerrit = gerrit_obj
|
1515
|
+
self.verbose = verbose
|
1516
|
+
self.dry_run = dry_run
|
1517
|
+
self.more_cc = []
|
1518
|
+
self.thread_pool = thread_pool
|
1519
|
+
self.parallel = parallel
|
1520
|
+
|
1521
|
+
def ExecPresubmitScript(self, script_text, presubmit_path):
|
1522
|
+
"""Executes a single presubmit script.
|
1523
|
+
|
1524
|
+
Args:
|
1525
|
+
script_text: The text of the presubmit script.
|
1526
|
+
presubmit_path: The path to the presubmit file (this will be reported via
|
1527
|
+
input_api.PresubmitLocalPath()).
|
1528
|
+
|
1529
|
+
Return:
|
1530
|
+
A list of result objects, empty if no problems.
|
1531
|
+
"""
|
1532
|
+
|
1533
|
+
# Change to the presubmit file's directory to support local imports.
|
1534
|
+
main_path = os.getcwd()
|
1535
|
+
os.chdir(os.path.dirname(presubmit_path))
|
1536
|
+
|
1537
|
+
# Load the presubmit script into context.
|
1538
|
+
input_api = InputApi(self.change, presubmit_path, self.committing,
|
1539
|
+
self.verbose, gerrit_obj=self.gerrit,
|
1540
|
+
dry_run=self.dry_run, thread_pool=self.thread_pool,
|
1541
|
+
parallel=self.parallel)
|
1542
|
+
output_api = OutputApi(self.committing)
|
1543
|
+
context = {}
|
1544
|
+
try:
|
1545
|
+
exec(compile(script_text, 'PRESUBMIT.py', 'exec', dont_inherit=True),
|
1546
|
+
context)
|
1547
|
+
except Exception as e:
|
1548
|
+
raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e))
|
1549
|
+
|
1550
|
+
# These function names must change if we make substantial changes to
|
1551
|
+
# the presubmit API that are not backwards compatible.
|
1552
|
+
if self.committing:
|
1553
|
+
function_name = 'CheckChangeOnCommit'
|
1554
|
+
else:
|
1555
|
+
function_name = 'CheckChangeOnUpload'
|
1556
|
+
if function_name in context:
|
1557
|
+
try:
|
1558
|
+
context['__args'] = (input_api, output_api)
|
1559
|
+
logging.debug('Running %s in %s', function_name, presubmit_path)
|
1560
|
+
result = eval(function_name + '(*__args)', context)
|
1561
|
+
logging.debug('Running %s done.', function_name)
|
1562
|
+
self.more_cc.extend(output_api.more_cc)
|
1563
|
+
finally:
|
1564
|
+
for f in input_api._named_temporary_files:
|
1565
|
+
os.remove(f)
|
1566
|
+
if not isinstance(result, (tuple, list)):
|
1567
|
+
raise PresubmitFailure(
|
1568
|
+
'Presubmit functions must return a tuple or list')
|
1569
|
+
for item in result:
|
1570
|
+
if not isinstance(item, OutputApi.PresubmitResult):
|
1571
|
+
raise PresubmitFailure(
|
1572
|
+
'All presubmit results must be of types derived from '
|
1573
|
+
'output_api.PresubmitResult')
|
1574
|
+
else:
|
1575
|
+
result = () # no error since the script doesn't care about current event.
|
1576
|
+
|
1577
|
+
# Return the process to the original working directory.
|
1578
|
+
os.chdir(main_path)
|
1579
|
+
return result
|
1580
|
+
|
1581
|
+
def DoPresubmitChecks(change,
|
1582
|
+
committing,
|
1583
|
+
verbose,
|
1584
|
+
default_presubmit,
|
1585
|
+
may_prompt,
|
1586
|
+
gerrit_obj,
|
1587
|
+
dry_run=None,
|
1588
|
+
parallel=False,
|
1589
|
+
json_output=None):
|
1590
|
+
"""Runs all presubmit checks that apply to the files in the change.
|
1591
|
+
|
1592
|
+
This finds all PRESUBMIT.py files in directories enclosing the files in the
|
1593
|
+
change (up to the repository root) and calls the relevant entrypoint function
|
1594
|
+
depending on whether the change is being committed or uploaded.
|
1595
|
+
|
1596
|
+
Prints errors, warnings and notifications. Prompts the user for warnings
|
1597
|
+
when needed.
|
1598
|
+
|
1599
|
+
Args:
|
1600
|
+
change: The Change object.
|
1601
|
+
committing: True if 'git cl land' is running, False if 'git cl upload' is.
|
1602
|
+
verbose: Prints debug info.
|
1603
|
+
default_presubmit: A default presubmit script to execute in any case.
|
1604
|
+
may_prompt: Enable (y/n) questions on warning or error. If False,
|
1605
|
+
any questions are answered with yes by default.
|
1606
|
+
gerrit_obj: provides basic Gerrit codereview functionality.
|
1607
|
+
dry_run: if true, some Checks will be skipped.
|
1608
|
+
parallel: if true, all tests specified by input_api.RunTests in all
|
1609
|
+
PRESUBMIT files will be run in parallel.
|
1610
|
+
|
1611
|
+
Return:
|
1612
|
+
1 if presubmit checks failed or 0 otherwise.
|
1613
|
+
"""
|
1614
|
+
old_environ = os.environ
|
1615
|
+
try:
|
1616
|
+
# Make sure python subprocesses won't generate .pyc files.
|
1617
|
+
os.environ = os.environ.copy()
|
1618
|
+
os.environ['PYTHONDONTWRITEBYTECODE'] = '1'
|
1619
|
+
|
1620
|
+
if committing:
|
1621
|
+
sys.stdout.write('Running presubmit commit checks ...\n')
|
1622
|
+
else:
|
1623
|
+
sys.stdout.write('Running presubmit upload checks ...\n')
|
1624
|
+
start_time = time_time()
|
1625
|
+
presubmit_files = ListRelevantPresubmitFiles(
|
1626
|
+
change.AbsoluteLocalPaths(), change.RepositoryRoot())
|
1627
|
+
if not presubmit_files and verbose:
|
1628
|
+
sys.stdout.write('Warning, no PRESUBMIT.py found.\n')
|
1629
|
+
results = []
|
1630
|
+
thread_pool = ThreadPool()
|
1631
|
+
executer = PresubmitExecuter(change, committing, verbose, gerrit_obj,
|
1632
|
+
dry_run, thread_pool, parallel)
|
1633
|
+
if default_presubmit:
|
1634
|
+
if verbose:
|
1635
|
+
sys.stdout.write('Running default presubmit script.\n')
|
1636
|
+
fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py')
|
1637
|
+
results += executer.ExecPresubmitScript(default_presubmit, fake_path)
|
1638
|
+
for filename in presubmit_files:
|
1639
|
+
filename = os.path.abspath(filename)
|
1640
|
+
if verbose:
|
1641
|
+
sys.stdout.write('Running %s\n' % filename)
|
1642
|
+
# Accept CRLF presubmit script.
|
1643
|
+
presubmit_script = gclient_utils.FileRead(filename, 'rU')
|
1644
|
+
results += executer.ExecPresubmitScript(presubmit_script, filename)
|
1645
|
+
|
1646
|
+
results += thread_pool.RunAsync()
|
1647
|
+
|
1648
|
+
messages = {}
|
1649
|
+
should_prompt = False
|
1650
|
+
presubmits_failed = False
|
1651
|
+
for result in results:
|
1652
|
+
if result.fatal:
|
1653
|
+
presubmits_failed = True
|
1654
|
+
messages.setdefault('ERRORS', []).append(result)
|
1655
|
+
elif result.should_prompt:
|
1656
|
+
should_prompt = True
|
1657
|
+
messages.setdefault('Warnings', []).append(result)
|
1658
|
+
else:
|
1659
|
+
messages.setdefault('Messages', []).append(result)
|
1660
|
+
|
1661
|
+
sys.stdout.write('\n')
|
1662
|
+
for name, items in messages.items():
|
1663
|
+
sys.stdout.write('** Presubmit %s **\n' % name)
|
1664
|
+
for item in items:
|
1665
|
+
item.handle()
|
1666
|
+
sys.stdout.write('\n')
|
1667
|
+
|
1668
|
+
total_time = time_time() - start_time
|
1669
|
+
if total_time > 1.0:
|
1670
|
+
sys.stdout.write(
|
1671
|
+
'Presubmit checks took %.1fs to calculate.\n\n' % total_time)
|
1672
|
+
|
1673
|
+
if not should_prompt and not presubmits_failed:
|
1674
|
+
sys.stdout.write('Presubmit checks passed.\n')
|
1675
|
+
elif should_prompt:
|
1676
|
+
sys.stdout.write('There were presubmit warnings. ')
|
1677
|
+
if may_prompt:
|
1678
|
+
presubmits_failed = not prompt_should_continue(
|
1679
|
+
'Are you sure you wish to continue? (y/N): ')
|
1680
|
+
|
1681
|
+
if json_output:
|
1682
|
+
# Write the presubmit results to json output
|
1683
|
+
presubmit_results = {
|
1684
|
+
'errors': [
|
1685
|
+
error.json_format()
|
1686
|
+
for error in messages.get('ERRORS', [])
|
1687
|
+
],
|
1688
|
+
'notifications': [
|
1689
|
+
notification.json_format()
|
1690
|
+
for notification in messages.get('Messages', [])
|
1691
|
+
],
|
1692
|
+
'warnings': [
|
1693
|
+
warning.json_format()
|
1694
|
+
for warning in messages.get('Warnings', [])
|
1695
|
+
],
|
1696
|
+
'more_cc': executer.more_cc,
|
1697
|
+
}
|
1698
|
+
|
1699
|
+
gclient_utils.FileWrite(
|
1700
|
+
json_output, json.dumps(presubmit_results, sort_keys=True))
|
1701
|
+
|
1702
|
+
global _ASKED_FOR_FEEDBACK
|
1703
|
+
# Ask for feedback one time out of 5.
|
1704
|
+
if (len(results) and random.randint(0, 4) == 0 and not _ASKED_FOR_FEEDBACK):
|
1705
|
+
sys.stdout.write(
|
1706
|
+
'Was the presubmit check useful? If not, run "git cl presubmit -v"\n'
|
1707
|
+
'to figure out which PRESUBMIT.py was run, then run git blame\n'
|
1708
|
+
'on the file to figure out who to ask for help.\n')
|
1709
|
+
_ASKED_FOR_FEEDBACK = True
|
1710
|
+
|
1711
|
+
return 1 if presubmits_failed else 0
|
1712
|
+
finally:
|
1713
|
+
os.environ = old_environ
|
1714
|
+
|
1715
|
+
|
1716
|
+
def _scan_sub_dirs(mask, recursive):
|
1717
|
+
if not recursive:
|
1718
|
+
return [x for x in glob.glob(mask) if x not in ('.svn', '.git')]
|
1719
|
+
|
1720
|
+
results = []
|
1721
|
+
for root, dirs, files in os.walk('.'):
|
1722
|
+
if '.svn' in dirs:
|
1723
|
+
dirs.remove('.svn')
|
1724
|
+
if '.git' in dirs:
|
1725
|
+
dirs.remove('.git')
|
1726
|
+
for name in files:
|
1727
|
+
if fnmatch.fnmatch(name, mask):
|
1728
|
+
results.append(os.path.join(root, name))
|
1729
|
+
return results
|
1730
|
+
|
1731
|
+
|
1732
|
+
def _parse_files(args, recursive):
|
1733
|
+
logging.debug('Searching for %s', args)
|
1734
|
+
files = []
|
1735
|
+
for arg in args:
|
1736
|
+
files.extend([('M', f) for f in _scan_sub_dirs(arg, recursive)])
|
1737
|
+
return files
|
1738
|
+
|
1739
|
+
|
1740
|
+
def _parse_change(parser, options):
|
1741
|
+
"""Process change options.
|
1742
|
+
|
1743
|
+
Args:
|
1744
|
+
parser: The parser used to parse the arguments from command line.
|
1745
|
+
options: The arguments parsed from command line.
|
1746
|
+
Returns:
|
1747
|
+
A GitChange if the change root is a git repository, or a Change otherwise.
|
1748
|
+
"""
|
1749
|
+
if options.files and options.all_files:
|
1750
|
+
parser.error('<files> cannot be specified when --all-files is set.')
|
1751
|
+
|
1752
|
+
change_scm = scm.determine_scm(options.root)
|
1753
|
+
if change_scm != 'git' and not options.files:
|
1754
|
+
parser.error('<files> is not optional for unversioned directories.')
|
1755
|
+
|
1756
|
+
if options.files:
|
1757
|
+
change_files = _parse_files(options.files, options.recursive)
|
1758
|
+
elif options.all_files:
|
1759
|
+
change_files = [('M', f) for f in scm.GIT.GetAllFiles(options.root)]
|
1760
|
+
else:
|
1761
|
+
change_files = scm.GIT.CaptureStatus(
|
1762
|
+
options.root, options.upstream or None)
|
1763
|
+
|
1764
|
+
logging.info('Found %d file(s).', len(change_files))
|
1765
|
+
|
1766
|
+
change_class = GitChange if change_scm == 'git' else Change
|
1767
|
+
return change_class(
|
1768
|
+
options.name,
|
1769
|
+
options.description,
|
1770
|
+
options.root,
|
1771
|
+
change_files,
|
1772
|
+
options.issue,
|
1773
|
+
options.patchset,
|
1774
|
+
options.author,
|
1775
|
+
upstream=options.upstream)
|
1776
|
+
|
1777
|
+
|
1778
|
+
def _parse_gerrit_options(parser, options):
|
1779
|
+
"""Process gerrit options.
|
1780
|
+
|
1781
|
+
SIDE EFFECTS: Modifies options.author and options.description from Gerrit if
|
1782
|
+
options.gerrit_fetch is set.
|
1783
|
+
|
1784
|
+
Args:
|
1785
|
+
parser: The parser used to parse the arguments from command line.
|
1786
|
+
options: The arguments parsed from command line.
|
1787
|
+
Returns:
|
1788
|
+
A GerritAccessor object if options.gerrit_url is set, or None otherwise.
|
1789
|
+
"""
|
1790
|
+
gerrit_obj = None
|
1791
|
+
if options.gerrit_url:
|
1792
|
+
gerrit_obj = GerritAccessor(urlparse.urlparse(options.gerrit_url).netloc)
|
1793
|
+
|
1794
|
+
if not options.gerrit_fetch:
|
1795
|
+
return gerrit_obj
|
1796
|
+
|
1797
|
+
if not options.gerrit_url or not options.issue or not options.patchset:
|
1798
|
+
parser.error(
|
1799
|
+
'--gerrit_fetch requires --gerrit_url, --issue and --patchset.')
|
1800
|
+
|
1801
|
+
options.author = gerrit_obj.GetChangeOwner(options.issue)
|
1802
|
+
options.description = gerrit_obj.GetChangeDescription(
|
1803
|
+
options.issue, options.patchset)
|
1804
|
+
|
1805
|
+
logging.info('Got author: "%s"', options.author)
|
1806
|
+
logging.info('Got description: """\n%s\n"""', options.description)
|
1807
|
+
|
1808
|
+
return gerrit_obj
|
1809
|
+
|
1810
|
+
|
1811
|
+
@contextlib.contextmanager
|
1812
|
+
def canned_check_filter(method_names):
|
1813
|
+
filtered = {}
|
1814
|
+
try:
|
1815
|
+
for method_name in method_names:
|
1816
|
+
if not hasattr(presubmit_canned_checks, method_name):
|
1817
|
+
logging.warn('Skipping unknown "canned" check %s' % method_name)
|
1818
|
+
continue
|
1819
|
+
filtered[method_name] = getattr(presubmit_canned_checks, method_name)
|
1820
|
+
setattr(presubmit_canned_checks, method_name, lambda *_a, **_kw: [])
|
1821
|
+
yield
|
1822
|
+
finally:
|
1823
|
+
for name, method in filtered.items():
|
1824
|
+
setattr(presubmit_canned_checks, name, method)
|
1825
|
+
|
1826
|
+
|
1827
|
+
def main(argv=None):
|
1828
|
+
parser = argparse.ArgumentParser(usage='%(prog)s [options] <files...>')
|
1829
|
+
hooks = parser.add_mutually_exclusive_group()
|
1830
|
+
hooks.add_argument('-c', '--commit', action='store_true',
|
1831
|
+
help='Use commit instead of upload checks.')
|
1832
|
+
hooks.add_argument('-u', '--upload', action='store_false', dest='commit',
|
1833
|
+
help='Use upload instead of commit checks.')
|
1834
|
+
hooks.add_argument('--post_upload', action='store_true',
|
1835
|
+
help='Run post-upload commit hooks.')
|
1836
|
+
parser.add_argument('-r', '--recursive', action='store_true',
|
1837
|
+
help='Act recursively.')
|
1838
|
+
parser.add_argument('-v', '--verbose', action='count', default=0,
|
1839
|
+
help='Use 2 times for more debug info.')
|
1840
|
+
parser.add_argument('--name', default='no name')
|
1841
|
+
parser.add_argument('--author')
|
1842
|
+
desc = parser.add_mutually_exclusive_group()
|
1843
|
+
desc.add_argument('--description', default='', help='The change description.')
|
1844
|
+
desc.add_argument('--description_file',
|
1845
|
+
help='File to read change description from.')
|
1846
|
+
parser.add_argument('--issue', type=int, default=0)
|
1847
|
+
parser.add_argument('--patchset', type=int, default=0)
|
1848
|
+
parser.add_argument('--root', default=os.getcwd(),
|
1849
|
+
help='Search for PRESUBMIT.py up to this directory. '
|
1850
|
+
'If inherit-review-settings-ok is present in this '
|
1851
|
+
'directory, parent directories up to the root file '
|
1852
|
+
'system directories will also be searched.')
|
1853
|
+
parser.add_argument('--upstream',
|
1854
|
+
help='Git only: the base ref or upstream branch against '
|
1855
|
+
'which the diff should be computed.')
|
1856
|
+
parser.add_argument('--default_presubmit')
|
1857
|
+
parser.add_argument('--may_prompt', action='store_true', default=False)
|
1858
|
+
parser.add_argument('--skip_canned', action='append', default=[],
|
1859
|
+
help='A list of checks to skip which appear in '
|
1860
|
+
'presubmit_canned_checks. Can be provided multiple times '
|
1861
|
+
'to skip multiple canned checks.')
|
1862
|
+
parser.add_argument('--dry_run', action='store_true', help=argparse.SUPPRESS)
|
1863
|
+
parser.add_argument('--gerrit_url', help=argparse.SUPPRESS)
|
1864
|
+
parser.add_argument('--gerrit_fetch', action='store_true',
|
1865
|
+
help=argparse.SUPPRESS)
|
1866
|
+
parser.add_argument('--parallel', action='store_true',
|
1867
|
+
help='Run all tests specified by input_api.RunTests in '
|
1868
|
+
'all PRESUBMIT files in parallel.')
|
1869
|
+
parser.add_argument('--json_output',
|
1870
|
+
help='Write presubmit errors to json output.')
|
1871
|
+
parser.add_argument('--all_files', action='store_true',
|
1872
|
+
help='Mark all files under source control as modified.')
|
1873
|
+
parser.add_argument('files', nargs='*',
|
1874
|
+
help='List of files to be marked as modified when '
|
1875
|
+
'executing presubmit or post-upload hooks. fnmatch '
|
1876
|
+
'wildcards can also be used.')
|
1877
|
+
|
1878
|
+
options = parser.parse_args(argv)
|
1879
|
+
|
1880
|
+
log_level = logging.ERROR
|
1881
|
+
if options.verbose >= 2:
|
1882
|
+
log_level = logging.DEBUG
|
1883
|
+
elif options.verbose:
|
1884
|
+
log_level = logging.INFO
|
1885
|
+
log_format = ('[%(levelname).1s%(asctime)s %(process)d %(thread)d '
|
1886
|
+
'%(filename)s] %(message)s')
|
1887
|
+
logging.basicConfig(format=log_format, level=log_level)
|
1888
|
+
|
1889
|
+
if options.description_file:
|
1890
|
+
options.description = gclient_utils.FileRead(options.description_file)
|
1891
|
+
gerrit_obj = _parse_gerrit_options(parser, options)
|
1892
|
+
change = _parse_change(parser, options)
|
1893
|
+
|
1894
|
+
try:
|
1895
|
+
if options.post_upload:
|
1896
|
+
return DoPostUploadExecuter(
|
1897
|
+
change,
|
1898
|
+
gerrit_obj,
|
1899
|
+
options.verbose)
|
1900
|
+
with canned_check_filter(options.skip_canned):
|
1901
|
+
return DoPresubmitChecks(
|
1902
|
+
change,
|
1903
|
+
options.commit,
|
1904
|
+
options.verbose,
|
1905
|
+
options.default_presubmit,
|
1906
|
+
options.may_prompt,
|
1907
|
+
gerrit_obj,
|
1908
|
+
options.dry_run,
|
1909
|
+
options.parallel,
|
1910
|
+
options.json_output)
|
1911
|
+
except PresubmitFailure as e:
|
1912
|
+
print(e, file=sys.stderr)
|
1913
|
+
print('Maybe your depot_tools is out of date?', file=sys.stderr)
|
1914
|
+
return 2
|
1915
|
+
|
1916
|
+
|
1917
|
+
if __name__ == '__main__':
|
1918
|
+
fix_encoding.fix_encoding()
|
1919
|
+
try:
|
1920
|
+
sys.exit(main())
|
1921
|
+
except KeyboardInterrupt:
|
1922
|
+
sys.stderr.write('interrupted\n')
|
1923
|
+
sys.exit(2)
|