blob: a960f06deaa701e6736c15bd483dc10a307d9752 [file]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import os
import pathlib
import shutil
import subprocess
import sys
import tempfile
import typing as T
import build_command_query_utils
import normalize_rustc_args
import shell_utils
# Enable debug logging.
_DEBUG = False
# Root directory of the Fuchsia source tree.
_FUCHSIA_DIR = pathlib.Path(__file__).parent.parent.parent.parent
# Path to the default Ninja binary.
_DEFAULT_NINJA_BIN = _FUCHSIA_DIR / "prebuilt/third_party/ninja/linux-x64/ninja"
sys.path.insert(0, str(_FUCHSIA_DIR / "build/api"))
import ninja_artifacts
sys.path.insert(0, str(_FUCHSIA_DIR / "build/bazel/scripts"))
import build_utils
def debug(s: T.Any) -> None:
if _DEBUG:
print(f"DEBUG: {s}", file=sys.stderr)
def try_get_build_dir(fuchsia_dir: pathlib.Path) -> T.Optional[pathlib.Path]:
"""
Try to get the build directory using `fx get-build-dir`.
Args:
fuchsia_dir: Path to the Fuchsia source tree.
Returns:
The build directory if it can be found, None otherwise.
"""
fx_build_dir_file = fuchsia_dir / ".fx-build-dir"
if not fx_build_dir_file.exists():
return None
return pathlib.Path(fx_build_dir_file.read_text().strip())
def main() -> int:
parser = argparse.ArgumentParser(
description="Compare GN and Bazel build commands for rustc."
)
parser.add_argument(
"--fuchsia_dir",
type=pathlib.Path,
default=_FUCHSIA_DIR,
help="Path to Fuchsia directory",
)
parser.add_argument(
"--build_dir",
type=pathlib.Path,
help="Path to GN build directory (e.g. out/default)",
)
parser.add_argument(
"--ninja_outputs_path",
type=pathlib.Path,
help="Path to ninja outputs JSON file",
)
parser.add_argument(
"--ninja_bin",
type=pathlib.Path,
default=_DEFAULT_NINJA_BIN,
help="Path to ninja binary",
)
parser.add_argument(
"--gn_label", required=True, help="GN Rust target label"
)
parser.add_argument(
"--bazel_label", required=True, help="Bazel Rust target label"
)
parser.add_argument(
"--read_response_files",
action="store_true",
default=False,
help="Read response files directly from the Bazel execroot instead of querying Starlark providers.",
)
parser.add_argument(
"--verbose",
"-v",
action="store_true",
default=False,
help="Print verbose output",
)
parser.add_argument(
"--temp_dir", type=pathlib.Path, help="Temporary directory path"
)
args = parser.parse_args()
global _DEBUG
_DEBUG = args.verbose
build_command_query_utils.set_debug(args.verbose)
build_dir = args.build_dir or try_get_build_dir(args.fuchsia_dir)
if not build_dir:
print(
"Error: Could not determine build directory. Please provide --build_dir."
)
return 1
ninja_outputs_path = args.ninja_outputs_path or (
build_dir / "ninja_outputs.json"
)
debug(f"Fuchsia Dir: {args.fuchsia_dir}")
debug(f"Build Dir: {build_dir}")
debug(f"Ninja outputs path: {ninja_outputs_path}")
debug(f"Ninja Path: {args.ninja_bin}")
debug(f"GN Label: {args.gn_label}")
debug(f"Bazel Label: {args.bazel_label}")
ninja_runner = ninja_artifacts.NinjaRunner(args.ninja_bin, build_dir)
gn_cmd_raw = build_command_query_utils.query_ninja_command(
ninja_runner, ninja_outputs_path, args.gn_label
)
gn_cmd = shell_utils.ShellCommand(gn_cmd_raw)
bazel_paths = build_utils.BazelPaths(args.fuchsia_dir, build_dir)
bazel_launcher = build_utils.BazelLauncher(bazel_paths.launcher)
bazel_cmd_raw = build_command_query_utils.query_bazel_command(
bazel_launcher,
bazel_paths.execroot,
args.bazel_label,
read_response_files=args.read_response_files,
)
bazel_cmd = shell_utils.ShellCommand(bazel_cmd_raw)
gn_rustc_cmd = shell_utils.find_command_with_tool(gn_cmd.split(), "rustc")
bazel_rustc_cmd = shell_utils.find_command_with_tool(
bazel_cmd.split(), "rustc"
)
debug("====== GN Command ======")
debug(gn_rustc_cmd)
debug("====== Bazel Command ======")
debug(bazel_rustc_cmd)
if not gn_rustc_cmd or not bazel_rustc_cmd:
print("Failed to get GN or Bazel rustc command.")
return 1
normalized_gn_args = normalize_rustc_args.normalize_rustc_cmd(
str(gn_rustc_cmd)
)
normalized_bazel_args = normalize_rustc_args.normalize_rustc_cmd(
str(bazel_rustc_cmd)
)
temp_dir = tempfile.mkdtemp(
prefix="compare_rustc_commands_",
dir=args.temp_dir,
)
gn_file = os.path.join(temp_dir, "normalized_gn_args.txt")
bazel_file = os.path.join(temp_dir, "normalized_bazel_args.txt")
with open(gn_file, "w") as f:
f.write("\n".join(normalized_gn_args) + "\n")
with open(bazel_file, "w") as f:
f.write("\n".join(normalized_bazel_args) + "\n")
debug(f"Comparing normalized args with command:")
debug(f"diff -u {gn_file} {bazel_file}")
result = subprocess.run(["diff", "-u", gn_file, bazel_file])
# Preserve temporary results if verbose mode or a temp dir is specified.
# In these modes, the user may want to inspect the temporary files.
if not (_DEBUG or args.temp_dir):
shutil.rmtree(temp_dir, ignore_errors=True)
return result.returncode
if __name__ == "__main__":
sys.exit(main())