diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 6b1c55daacd27..def61b2844306 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -17,4 +17,4 @@ jobs: - uses: actions/setup-python@v2 - uses: pre-commit/action@v2.0.2 env: - SKIP: bazel-buildifier,clang-format-whole-file + SKIP: bazel-buildifier,clang-format-whole-file,fix-cc-deps diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba96b7ebd8e8d..d721e9d30838f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -98,6 +98,12 @@ repos: language: python files: ^executable_semantics/syntax/(lexer.lpp|parser.ypp)$ pass_filenames: false + - id: fix-cc-deps + name: Fix missing C++ deps + entry: scripts/fix_cc_deps.py + language: python + files: ^.*/(BUILD|[^/]+\.(h|cpp))$ + pass_filenames: false # Formatters should be run late so that they can re-format any prior changes. - repo: https://github.com/psf/black rev: 19f6aa8208154de4560ee1e4a3e638e120dcdba5 # frozen: 21.11b1 diff --git a/scripts/fix_cc_deps.py b/scripts/fix_cc_deps.py index 34f216adf62ca..8d0665e322912 100755 --- a/scripts/fix_cc_deps.py +++ b/scripts/fix_cc_deps.py @@ -19,6 +19,7 @@ import shutil import subprocess from pathlib import Path +import tempfile from typing import Callable, Dict, List, NamedTuple, Set, Tuple from xml.etree import ElementTree @@ -44,6 +45,29 @@ class Rule(NamedTuple): outs: Set[str] +def install_buildozer() -> str: + # 4.2.4 + buildozer_sha = "cdedcc0318b9c8919afb0167e30c1588fc990ffc" + args = [ + "go", + "install", + f"github.com/bazelbuild/buildtools/buildozer@{buildozer_sha}", + ] + # Install to a cache. + env = os.environ.copy() + if "XDG_CACHE_HOME" in env: + cache_dir = Path(env["XDG_CACHE_HOME"]) + else: + cache_dir = Path(tempfile.gettempdir()) + cache_dir = cache_dir.joinpath("carbon-pre-commit-cache") + cache_dir.mkdir(parents=True, exist_ok=True) + env["GOPATH"] = str(cache_dir) + if "GOBIN" in env: + del env["GOBIN"] + subprocess.check_call(args, env=env) + return str(cache_dir.joinpath("bin", "buildozer")) + + def locate_bazel() -> str: """Returns the bazel command. @@ -190,7 +214,8 @@ def get_missing_deps( f"{', '.join(dep_choices)}" ) ambiguous = True - missing_deps.add(dep_choices.pop()) + # Use the single dep without removing it. + missing_deps.add(next(iter(dep_choices))) return missing_deps, ambiguous @@ -229,13 +254,23 @@ def main() -> None: if any_ambiguous: exit("Stopping due to ambiguous dependency choices.") - print("Fixing dependencies...") - SEPARATOR = "\n- " - for rule_name, missing_deps in sorted(all_missing_deps): - friendly_missing_deps = SEPARATOR.join(missing_deps) - print(f"Adding deps to {rule_name}:{SEPARATOR}{friendly_missing_deps}") - args = ["buildozer", f"add deps {' '.join(missing_deps)}", rule_name] - subprocess.check_call(args) + if all_missing_deps: + print("Checking buildozer availability...") + buildozer = install_buildozer() + + print("Fixing dependencies...") + SEPARATOR = "\n- " + for rule_name, missing_deps in sorted(all_missing_deps): + friendly_missing_deps = SEPARATOR.join(missing_deps) + print( + f"Adding deps to {rule_name}:{SEPARATOR}{friendly_missing_deps}" + ) + args = [ + buildozer, + f"add deps {' '.join(missing_deps)}", + rule_name, + ] + subprocess.check_call(args) print("Done!")