Files
TheRock/external-builds/pytorch/checkout_from_manifest.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

251 lines
7.5 KiB
Python

#!/usr/bin/env python3
# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""Check out pytorch ecosystem repositories from a manifest file.
Reads a manifest JSON (produced by ``generate_pytorch_source_manifest.py``)
and calls the individual ``pytorch_*_repo.py checkout`` scripts with the
exact commit SHAs and repo URLs from the manifest.
This provides a single command to reproduce the same source tree that CI
builds use, given only a manifest file.
Usage::
# Check out all projects in the manifest under ./checkouts/:
python checkout_from_manifest.py \\
--manifest manifests/therock-manifest_torch_linux_release-2.10.json \\
--checkout-root ./checkouts
# Check out only pytorch (skip audio, vision, etc.):
python checkout_from_manifest.py \\
--manifest manifest.json \\
--checkout-root ./checkouts \\
--projects pytorch
"""
import argparse
import json
import subprocess
import sys
import urllib.request
from pathlib import Path
THIS_DIR = Path(__file__).resolve().parent
SCHEMA_VERSION = 1
# Maps manifest project names to their checkout scripts.
CHECKOUT_SCRIPTS: dict[str, str] = {
"pytorch": "pytorch_torch_repo.py",
"pytorch_audio": "pytorch_audio_repo.py",
"pytorch_vision": "pytorch_vision_repo.py",
"triton": "pytorch_triton_repo.py",
"apex": "pytorch_apex_repo.py",
}
def _split_words(value: str) -> list[str]:
return value.replace(";", " ").split() if value else []
def log(*args, **kwargs) -> None:
print(*args, **kwargs)
sys.stdout.flush()
def checkout_project(
*,
name: str,
source_info: dict[str, str],
checkout_root: Path,
no_hipify: bool,
) -> None:
"""Check out a single project using its checkout script."""
script = THIS_DIR / CHECKOUT_SCRIPTS[name]
checkout_dir = checkout_root / name
commit = source_info["commit"]
repo = source_info["repo"]
log(f" {name}: {repo} @ {commit[:12]}")
cmd: list[str] = [
sys.executable,
str(script),
"checkout",
"--gitrepo-origin",
repo,
"--repo-hashtag",
commit,
"--checkout-dir",
str(checkout_dir),
]
if no_hipify:
cmd.append("--no-hipify")
log(f" Running: {' '.join(cmd)}")
subprocess.check_call(cmd)
def download_manifest(*, manifest_url: str, output_path: Path) -> Path:
"""Download a manifest URL to output_path."""
output_path.parent.mkdir(parents=True, exist_ok=True)
log(f"Downloading manifest: {manifest_url} -> {output_path}")
urllib.request.urlretrieve(manifest_url, output_path)
if not output_path.is_file() or output_path.stat().st_size == 0:
raise RuntimeError(f"Failed to download manifest: {manifest_url}")
return output_path
def load_manifest(manifest_path: Path) -> dict[str, object]:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
if not isinstance(manifest, dict):
raise ValueError("Manifest root must be a JSON object")
schema_version = manifest.get("schema_version")
if schema_version is not None and schema_version != SCHEMA_VERSION:
raise ValueError(
f"Unsupported manifest schema_version {schema_version!r}; "
f"expected {SCHEMA_VERSION}"
)
return manifest
def require_project_source_info(
manifest: dict[str, object], project: str
) -> dict[str, str]:
source_info = manifest.get(project)
if not isinstance(source_info, dict):
raise ValueError(f"Manifest entry {project!r} must be a JSON object")
for field in ("repo", "commit"):
value = source_info.get(field)
if not isinstance(value, str) or not value:
raise ValueError(
f"Manifest entry {project!r} is missing required string "
f"field {field!r}"
)
return {
str(field): value
for field, value in source_info.items()
if isinstance(value, str)
}
def validate_manifest(
manifest: dict[str, object], *, expected_pytorch_git_ref: str
) -> None:
"""Validate manifest contents against workflow expectations."""
if not expected_pytorch_git_ref:
return
pytorch = manifest.get("pytorch")
if not isinstance(pytorch, dict):
raise ValueError("Manifest is missing pytorch entry")
actual_ref = pytorch.get("branch")
if actual_ref != expected_pytorch_git_ref:
raise ValueError(
f"Manifest PyTorch ref {actual_ref!r} does not match "
f"{expected_pytorch_git_ref!r}"
)
def main(argv: list[str]) -> None:
parser = argparse.ArgumentParser(
description="Check out pytorch repos from a manifest file"
)
manifest_input = parser.add_mutually_exclusive_group(required=True)
manifest_input.add_argument(
"--manifest",
type=Path,
help="Path to manifest JSON file",
)
manifest_input.add_argument(
"--manifest-url",
help="URL to a manifest JSON file to download before checkout",
)
parser.add_argument(
"--checkout-root",
type=Path,
required=True,
help="Root directory for checkouts (each project gets a subdirectory)",
)
parser.add_argument(
"--projects",
default="",
help=(
"Semicolon- or space-separated list of projects to check out "
"(default: all projects in the manifest)."
),
)
parser.add_argument(
"--expected-pytorch-git-ref",
default="",
help="Validate manifest pytorch.branch against this ref before checkout",
)
parser.add_argument(
"--no-hipify",
action="store_true",
default=False,
help="Skip HIPIFY for all checkouts (e.g. for test-only runs)",
)
args = parser.parse_args(argv)
checkout_root = args.checkout_root.resolve()
manifest_path = args.manifest
if args.manifest_url:
manifest_path = checkout_root / "pytorch_manifest.json"
download_manifest(manifest_url=args.manifest_url, output_path=manifest_path)
manifest = load_manifest(manifest_path)
validate_manifest(manifest, expected_pytorch_git_ref=args.expected_pytorch_git_ref)
# Determine which projects to check out.
available = [name for name in CHECKOUT_SCRIPTS if name in manifest]
if args.projects:
projects = _split_words(args.projects)
unknown = set(projects) - set(available)
if unknown:
parser.error(
f"Projects not in manifest: {unknown}. " f"Available: {available}"
)
else:
projects = available
if not projects:
supported = ", ".join(CHECKOUT_SCRIPTS)
raise ValueError(
"Manifest contains no supported PyTorch projects. "
f"Supported projects: {supported}"
)
source_infos = {
name: require_project_source_info(manifest, name) for name in projects
}
log(f"Manifest: {manifest_path}")
log(f"Checkout root: {checkout_root}")
log(f"Projects: {projects}")
log("")
checkout_root.mkdir(parents=True, exist_ok=True)
# Keep pytorch first so a full checkout tree is easy to inspect.
if "pytorch" in projects:
projects = ["pytorch"] + [p for p in projects if p != "pytorch"]
for name in projects:
checkout_project(
name=name,
source_info=source_infos[name],
checkout_root=checkout_root,
no_hipify=args.no_hipify,
)
log("")
log("All checkouts complete.")
if __name__ == "__main__":
main(sys.argv[1:])