blob: 920485984ccd2ea02e7198cd142692d1d6c45c95 [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.
"""Fetch an on-demand CIPD prebuilt package via `jiri fetch-package` and materialize it in bazel-out/."""
import argparse
import os
import shutil
import subprocess
import sys
from collections.abc import Sequence
from pathlib import Path
_SCRIPT_DIR = Path(__file__).resolve().parent
_DEFAULT_FUCHSIA_DIR = _SCRIPT_DIR.parent.parent.parent
def _link_or_copy_file(src: str | Path, dst: str | Path) -> None:
"""Materialize a file from `src` to `dst` using a hardlink, falling back to copy."""
src_path = Path(src).resolve()
dst_path = Path(dst)
dst_path.parent.mkdir(parents=True, exist_ok=True)
if dst_path.exists() or dst_path.is_symlink():
dst_path.unlink()
try:
os.link(src_path, dst_path)
except OSError:
shutil.copy2(src_path, dst_path)
def _materialize_directory(src_dir: Path, dst_dir: Path) -> None:
"""Materialize a directory tree from `src_dir` to `dst_dir` using hardlinks or copies."""
src_dir_resolved = src_dir.resolve()
if dst_dir.exists() or dst_dir.is_symlink():
if dst_dir.is_dir() and not dst_dir.is_symlink():
shutil.rmtree(dst_dir)
else:
dst_dir.unlink()
dst_dir.mkdir(parents=True, exist_ok=True)
shutil.copytree(
src_dir_resolved,
dst_dir,
symlinks=True,
copy_function=_link_or_copy_file,
dirs_exist_ok=True,
)
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--path",
required=True,
help="Prebuilt path relative to Fuchsia root (e.g. 'prebuilt/third_party/...').",
)
parser.add_argument(
"--fuchsia-dir",
type=Path,
default=None,
help="Path to Fuchsia root directory (defaults to FUCHSIA_DIR env var or inferred from script path).",
)
output_group = parser.add_mutually_exclusive_group(required=True)
output_group.add_argument(
"--output-file",
type=Path,
help="Destination path for a single file output.",
)
output_group.add_argument(
"--output-dir",
type=Path,
help="Destination path for a directory output.",
)
args = parser.parse_args(argv)
if args.fuchsia_dir is not None:
fuchsia_dir = args.fuchsia_dir.resolve()
elif "FUCHSIA_DIR" in os.environ:
fuchsia_dir = Path(os.environ["FUCHSIA_DIR"]).resolve()
else:
fuchsia_dir = _DEFAULT_FUCHSIA_DIR
if not (fuchsia_dir / ".jiri_root").is_dir():
print(
f"ERROR: Fuchsia root directory '{fuchsia_dir}' does not contain a '.jiri_root' directory.",
file=sys.stderr,
)
return 1
norm_path = args.path.removeprefix("//").strip("/")
if not norm_path.startswith("prebuilt/"):
print(
f"ERROR: Prebuilt path must start with 'prebuilt/', got: {args.path}",
file=sys.stderr,
)
return 1
jiri_bin = fuchsia_dir / ".jiri_root" / "bin" / "jiri"
if not jiri_bin.is_file():
print(
f"ERROR: jiri binary not found at {jiri_bin}",
file=sys.stderr,
)
return 1
cmd = [str(jiri_bin), "fetch-package", norm_path]
result = subprocess.run(
cmd,
cwd=fuchsia_dir,
capture_output=True,
text=True,
)
if result.returncode != 0:
print(
f"ERROR: Command {cmd} failed with exit code {result.returncode}:\n"
f"stdout:\n{result.stdout}\n"
f"stderr:\n{result.stderr}",
file=sys.stderr,
)
return result.returncode
src_path = fuchsia_dir / norm_path
if not src_path.exists():
print(
f"ERROR: Expected prebuilt path does not exist after fetching: {src_path}",
file=sys.stderr,
)
return 1
if args.output_file is not None:
if not src_path.is_file():
print(
f"ERROR: Expected a file at {src_path}, but found a directory. "
"Use `cipd_prebuilt_directory` instead of `cipd_prebuilt_file`.",
file=sys.stderr,
)
return 1
_link_or_copy_file(src_path, args.output_file)
elif args.output_dir is not None:
if not src_path.is_dir():
print(
f"ERROR: Expected a directory at {src_path}, but found a file. "
"Use `cipd_prebuilt_file` instead of `cipd_prebuilt_directory`.",
file=sys.stderr,
)
return 1
_materialize_directory(src_path, args.output_dir)
return 0
if __name__ == "__main__":
sys.exit(main())