123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- # Copyright Materialize, Inc. and contributors. All rights reserved.
- #
- # Use of this software is governed by the Business Source License
- # included in the LICENSE file at the root of this repository.
- #
- # As of the Change Date specified in that file, in accordance with
- # the Business Source License, use of this software will be governed
- # by the Apache License, Version 2.0.
- #
- # bazel.py — build and test with bazel
- import argparse
- import os
- import pathlib
- import subprocess
- from pathlib import Path
- from materialize import MZ_ROOT, bazel, ui
- from materialize.bazel import remote_cache_arg
- from materialize.build_config import BuildConfig
- def main() -> int:
- parser = argparse.ArgumentParser(
- prog="bazel",
- description="Build, run, test, and generate packages with Bazel.",
- )
- parser.add_argument("action", help="Action to run.")
- (args, sub_args) = parser.parse_known_args()
- config = BuildConfig.read()
- # Always update our side-channel git hash incase some command needs it.
- bazel.write_git_hash()
- if args.action == "gen":
- gen_cmd(config, sub_args)
- elif args.action == "fmt":
- fmt_cmd(config, sub_args)
- elif args.action == "output_path":
- output_path_cmd(config, sub_args)
- elif args.action == "check":
- check_cmd(config, sub_args)
- elif args.action == "integrity":
- integrity_cmd(config, sub_args)
- else:
- bazel_cmd(config, [args.action] + sub_args)
- return 0
- def check_cmd(config: BuildConfig, args: list[str]):
- """
- Invokes a `bazel build` with `cargo check` like behavior.
- Still experimental, is known to fail with crates that have pipelined compilation explicitly
- disabled.
- """
- check_args = ["build", "--config=check", *args]
- bazel_cmd(config, check_args)
- def gen_cmd(config: BuildConfig, args: list[str]):
- """Invokes the gen function."""
- parser = argparse.ArgumentParser(
- prog="gen", description="Generate BUILD.bazel files."
- )
- parser.add_argument("--check", action="store_true")
- parser.add_argument(
- "path",
- type=pathlib.Path,
- help="Path to a Cargo.toml file to generate a BUILD.bazel for.",
- nargs="?",
- )
- gen_args = parser.parse_args(args=args)
- if gen_args.path:
- path = Path(os.path.abspath(gen_args.path))
- else:
- path = None
- gen(config, path, gen_args.check)
- def fmt_cmd(config: BuildConfig, args: list[str]):
- """Invokes the fmt function."""
- assert len(args) <= 1, "expected at most one path to format"
- path = args[0] if len(args) == 1 else None
- fmt(config, path)
- def output_path_cmd(config: BuildConfig, args: list[str]):
- """Invokes the output_path function."""
- assert len(args) == 1, "expected a single Bazel target"
- target = args[0]
- paths = bazel.output_paths(target)
- for path in paths:
- print(path)
- def integrity_cmd(config: BuildConfig, args: list[str]):
- """Calculate the integrity value for a file."""
- if args[0] == "toolchains":
- assert len(args) == 3, "expected <stable version> <nightly version>"
- stable = args[1]
- nightly = args[2]
- hashes = bazel.toolchain_hashes(stable, nightly)
- print(hashes)
- else:
- for path in args:
- integrity = bazel.calc_ingerity(path)
- print(integrity)
- def bazel_cmd(config: BuildConfig, args: list[str]):
- """Forwards all arguments to Bazel, possibly with extra configuration."""
- remote_cache = remote_cache_arg(config)
- try:
- subprocess.run(["bazel", *args, *remote_cache], check=True)
- except:
- # Don't print any python backtrace because it's never useful. Instead
- # just exit the process.
- exit(1)
- def gen(config: BuildConfig, path: Path | None, check: bool):
- """
- Generates BUILD.bazel files from Cargo.toml.
- Defaults to generating for the entire Cargo Workspace, or only a single
- Cargo.toml, if a path is provided.
- """
- if not path:
- path = MZ_ROOT / "Cargo.toml"
- check_arg = []
- if check:
- check_arg += ["--check"]
- remote_cache = remote_cache_arg(config)
- cmd_args = [
- "bazel",
- "run",
- *remote_cache,
- # TODO(parkmycar): Once bin/bazel gen is more stable in CI, enable this
- # config to make the output less noisy.
- # "--config=script",
- "//misc/bazel/tools:cargo-gazelle",
- "--",
- *check_arg,
- f"{str(path)}",
- ]
- subprocess.run(cmd_args, check=True)
- def fmt(config: BuildConfig, path):
- """
- Formats all of the `BUILD`, `.bzl`, and `WORKSPACE` files at the provided path.
- Defaults to formatting the entire Materialize repository.
- """
- if not path:
- path = MZ_ROOT
- if subprocess.run(["which", "bazel"]).returncode != 0:
- ui.warn("couldn't find 'bazel' skipping formatting of BUILD files")
- return
- # Note: No remote cache is needed here since we're just running an already
- # built binary.
- cmd_args = [
- "bazel",
- "run",
- "--config=script",
- "//misc/bazel/tools:buildifier",
- "--",
- "-r",
- f"{str(path)}",
- ]
- subprocess.run(cmd_args, check=True)
- def output_path(target) -> list[pathlib.Path]:
- """Returns the absolute path of the Bazel target."""
- cmd_args = ["bazel", "cquery", f"{target}", "--output=files"]
- paths = subprocess.check_output(
- cmd_args, text=True, stderr=subprocess.DEVNULL
- ).splitlines()
- return [pathlib.Path(path) for path in paths]
- if __name__ == "__main__":
- main()
|