Files
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

795 lines
28 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""Given ROCm artifacts directories, performs packaging to
create RPM and DEB packages and upload to artifactory server
```
# Standard release build (auto-detection of targets from artifact directory):
./build_package.py --artifacts-dir ./ARTIFACTS_DIR \
--dest-dir ./OUTPUT_PKGDIR \
--rocm-version 7.15.0 \
--pkg-type deb (or rpm) \
--version-suffix 28484694006
# ASAN build — package name gets -asan suffix, install prefix gets -asan-MAJOR.MINOR:
# Package: amdrocm-core-asan7.15
# Install prefix: /opt/rocm/core-asan-7.15
./build_package.py --artifacts-dir ./ARTIFACTS_DIR \
--dest-dir ./OUTPUT_PKGDIR \
--rocm-version 7.15.0 \
--pkg-type deb (or rpm) \
--version-suffix 28484694006 \
--build-variant asan
```
--version-suffix: CI run ID appended to the DEB/RPM version field
(e.g. '7.15.0-28484694006' for DEB, release='28484694006' for RPM).
Does not affect the package name or install prefix.
--build-variant: Build type that modifies both the package name and
install prefix. Currently supports 'asan'.
"""
import argparse
import json
import os
import sys
import traceback
from dataclasses import replace
from pathlib import Path
# Setup paths
SCRIPT_DIR = Path(__file__).resolve().parent
BUILD_TOOLS_DIR = SCRIPT_DIR.parent.parent
# Add build_tools directory to Python path to import _therock_utils
# This allows the script to be run from anywhere: TheRock root or packaging/linux directory
if str(BUILD_TOOLS_DIR) not in sys.path:
sys.path.insert(0, str(BUILD_TOOLS_DIR))
from packaging_summary import *
from packaging_utils import *
from runpath_to_rpath import *
from _therock_utils.artifacts import ArtifactCatalog
from deb_package import *
from rpm_package import *
# Default install prefix
DEFAULT_INSTALL_PREFIX = "/opt/rocm/core"
def load_kpack_from_manifest(artifacts_dir: Path) -> bool:
"""Detect kpack mode by scanning therock_manifest.json files in artifact directory.
Returns True if any manifest has KPACK_SPLIT_ARTIFACTS set to True.
"""
for manifest_path in artifacts_dir.rglob("therock_manifest.json"):
try:
manifest = json.loads(manifest_path.read_text())
if manifest.get("flags", {}).get("KPACK_SPLIT_ARTIFACTS", False):
return True
except (json.JSONDecodeError, OSError):
pass
return False
def get_all_target_families(artifact_dir):
"""Extract the list of GFX architectures from artifact directory.
Auto-detects available GFX architectures by scanning the artifact directory
for directories matching the pattern {name}_{component}_{target_family}.
Used for CLI input detection when --target is not explicitly provided.
Parameters:
artifact_dir : The path to the Artifactory directory
Returns:
list : Sorted list of unique GFX architectures (e.g., ["gfx1100", "gfx942"])
Raises:
ValueError: If artifact directory does not exist
"""
artifact_dir = Path(artifact_dir)
if not artifact_dir.exists() or not artifact_dir.is_dir():
raise ValueError(f"Artifact directory does not exist: {artifact_dir}")
# Use ArtifactCatalog from _therock_utils to get all target families
catalog = ArtifactCatalog(artifact_dir)
# Strip xnack suffixes (e.g., gfx942:xnack+ -> gfx942)
targets = {t.split(":", 1)[0] for t in catalog.all_target_families}
return sorted(targets)
################### Package Variant Builders #######################
def build_package_variants(pkg_name, config: PackageConfig) -> list:
"""Build all required package variants based on package type and mode.
This is the main entry point for building packages. It determines which
variants to build based on:
- Whether the package is a gfxarch package (GfxArch=True/False)
- Whether kpack mode is enabled
- Package type (DEB/RPM)
Parameters:
pkg_name: Name of the package to build
config: Configuration object containing package metadata
Returns:
List of built package filenames
Variants created:
For GfxArch=False (non-gfxarch packages):
- Versioned package (e.g., amdrocm-core8.2)
- Non-versioned package (e.g., amdrocm-core)
For GfxArch=True (gfxarch packages) in kpack mode (multi-arch):
- Host package (e.g., amdrocm-fft-host8.2)
- Device packages (e.g., amdrocm-fft8.2-gfx1100, amdrocm-fft8.2-gfx94x)
- Meta package (e.g., amdrocm-fft8.2)
- Non-versioned package (e.g., amdrocm-fft)
For GfxArch=True (gfxarch packages) in single-arch mode:
- Versioned package with arch suffix (e.g., amdrocm-fft8.2-gfx1100)
- Non-versioned package with arch suffix (e.g., amdrocm-fft-gfx1100)
"""
pkg_info = get_package_info(pkg_name) # Raises ValueError if not found
if config.enable_kpack:
if is_gfxarch_package(pkg_info, config.enable_kpack, config.artifacts_dir):
# GfxArch=True: host + devices + meta + non-versioned
return build_gfxarch_package_variants(pkg_name, config)
else:
# GfxArch=False: versioned + non-versioned
return build_simple_package_variants(pkg_name, config)
else:
# Single-arch mode
return build_singlearch_package_variants(pkg_name, config)
def build_gfxarch_package_variants(pkg_name, config: PackageConfig) -> list:
"""Build all variants for a gfxarch package in kpack mode (multi-arch).
For regular gfxarch packages (Metapackage=False), creates:
- Host package (e.g., amdrocm-fft-host8.2) - generic artifacts
- Device packages (e.g., amdrocm-fft8.2-gfx1100, amdrocm-fft8.2-gfx94x) - arch-specific artifacts
- Meta package (e.g., amdrocm-fft8.2) - depends on host + all devices
- Non-versioned package (e.g., amdrocm-fft) - user-facing, depends on meta
For gfxarch metapackages (Metapackage=True + Gfxarch=True), creates:
- Arch-specific meta packages (e.g., amdrocm-core8.2-gfx1100) - depends on actual packages
- Generic meta package (e.g., amdrocm-core8.2) - depends on all arch-specific metas
- Non-versioned package (e.g., amdrocm-core) - user-facing, depends on generic meta
(No host package - metapackages have no artifacts to split)
This function builds packages sequentially but is parallel-ready. Each variant
builder is independent (no shared state, no cleanup during build) and can be
run in parallel in the future. Cleanup is deferred until all variants complete.
Parameters:
pkg_name: Name of the package to build
config: Configuration object
Returns:
List of built package filenames
"""
built_packages = []
pkg_info = get_package_info(pkg_name)
is_meta = is_meta_package(pkg_info)
# Host package (contains generic artifacts)
# Skip for metapackages - they have no artifacts, only dependencies
if not is_meta:
print(f"\n=== Building host variant for {pkg_name} ===")
pkg = build_host_package(pkg_name, config)
if pkg:
built_packages.extend(pkg)
# Device packages (one per architecture)
# For metapackages, these become arch-specific meta packages
for device_arch in config.gfxarch_list:
print(f"\n=== Building device variant for {pkg_name} ({device_arch}) ===")
pkg = build_device_package(pkg_name, config, device_arch)
if pkg:
built_packages.extend(pkg)
# Meta package (depends on host + all devices for regular packages,
# or depends on all arch-specific metas for metapackages)
print(f"\n=== Building meta variant for {pkg_name} ===")
pkg = build_meta_package(pkg_name, config)
if pkg:
built_packages.extend(pkg)
# Non-versioned package (user-facing, depends on meta)
print(f"\n=== Building non-versioned variant for {pkg_name} ===")
# For gfxarch packages in kpack mode, non-versioned has no arch suffix (e.g., amdrocm-fft)
# It depends on the meta package which pulls in host + all devices
meta_config = replace(config, gfx_arch=GFX_META)
pkg = build_nonversioned_package(pkg_name, meta_config)
if pkg:
built_packages.extend(pkg)
cleanup_build_directory(config)
return built_packages
def build_simple_package_variants(pkg_name, config: PackageConfig) -> list:
"""Build variants for a non-gfxarch package in kpack mode.
Creates:
- Versioned package (e.g., amdrocm-core8.2)
- Non-versioned package with no arch suffix (e.g., amdrocm-core)
Parameters:
pkg_name: Name of the package to build
config: Configuration object
Returns:
List of built package filenames
"""
built_packages = []
# Versioned package
print(f"\n=== Building versioned variant for {pkg_name} ===")
pkg = build_versioned_package(pkg_name, config)
if pkg:
built_packages.extend(pkg)
# Non-versioned package
print(f"\n=== Building non-versioned variant for {pkg_name} ===")
# For non-gfxarch packages, non-versioned has no arch suffix (e.g., amdrocm-core)
simple_config = replace(config, gfx_arch="")
pkg = build_nonversioned_package(pkg_name, simple_config)
if pkg:
built_packages.extend(pkg)
cleanup_build_directory(config)
return built_packages
def build_singlearch_package_variants(pkg_name, config: PackageConfig) -> list:
"""Build package variants in single-arch mode (non-kpack).
Creates:
- Versioned package (e.g., amdrocm-core8.2 or amdrocm-fft8.2-gfx1100 for gfxarch packages)
- Non-versioned package (e.g., amdrocm-core or amdrocm-fft-gfx1100 for gfxarch packages)
In single-arch mode, gfxarch packages include the arch suffix in both variants
because they're specific to that architecture.
Parameters:
pkg_name: Name of the package to build
config: Configuration object
Returns:
List of built package filenames
"""
built_packages = []
# Versioned package
print(f"\n=== Building versioned package for {pkg_name} (single-arch mode) ===")
try:
versioned_config = replace(config, versioned_pkg=True)
if versioned_config.pkg_type == "rpm":
pkg = create_versioned_rpm_package(pkg_name, versioned_config)
else:
pkg = create_versioned_deb_package(pkg_name, versioned_config)
if pkg:
built_packages.extend(pkg)
except Exception as e:
print(f"ERROR: Failed to build versioned package for {pkg_name}: {e}")
# Non-versioned package
print(f"\n=== Building non-versioned package for {pkg_name} (single-arch mode) ===")
try:
# In single-arch mode, non-versioned packages keep the arch suffix
# to indicate they're specific to that architecture (e.g., amdrocm-fft-gfx1100)
nonversioned_config = replace(config, versioned_pkg=False)
if nonversioned_config.pkg_type == "rpm":
pkg = create_nonversioned_rpm_package(pkg_name, nonversioned_config)
else:
pkg = create_nonversioned_deb_package(pkg_name, nonversioned_config)
if pkg:
built_packages.extend(pkg)
except Exception as e:
print(f"ERROR: Failed to build non-versioned package for {pkg_name}: {e}")
cleanup_build_directory(config)
return built_packages
def build_host_package(pkg_name, config: PackageConfig) -> list:
"""Build host package variant (contains generic artifacts).
The host package contains architecture-independent artifacts and is named
with a -host suffix (e.g., amdrocm-fft-host8.2).
Parameters:
pkg_name: Name of the package to build
config: Configuration object
Returns:
List of built package filenames
"""
host_config = replace(config, gfx_arch=GFX_HOST, versioned_pkg=True)
try:
if host_config.pkg_type == "rpm":
return create_versioned_rpm_package(pkg_name, host_config)
else:
return create_versioned_deb_package(pkg_name, host_config)
except Exception as e:
print(f"ERROR: Failed to build host package for {pkg_name}: {e}")
return []
def build_device_package(pkg_name, config: PackageConfig, device_arch: str) -> list:
"""Build device-specific package variant.
Device packages contain architecture-specific artifacts and are named
with an architecture suffix (e.g., amdrocm-fft8.2-gfx1100).
Parameters:
pkg_name: Name of the package to build
config: Configuration object
device_arch: Device architecture (e.g., "gfx1100", "gfx94x")
Returns:
List of built package filenames
"""
device_config = replace(config, gfx_arch=device_arch, versioned_pkg=True)
try:
if device_config.pkg_type == "rpm":
return create_versioned_rpm_package(pkg_name, device_config)
else:
return create_versioned_deb_package(pkg_name, device_config)
except Exception as e:
print(
f"ERROR: Failed to build device package for {pkg_name} ({device_arch}): {e}"
)
return []
def build_meta_package(pkg_name, config: PackageConfig) -> list:
"""Build meta package that depends on host + all devices.
The meta package is a versioned metapackage with no content, only dependencies.
It pulls in the host package and all device packages (e.g., amdrocm-fft8.2).
Parameters:
pkg_name: Name of the package to build
config: Configuration object
Returns:
List of built package filenames
"""
meta_config = replace(config, gfx_arch=GFX_META, versioned_pkg=True)
try:
if meta_config.pkg_type == "rpm":
return create_versioned_rpm_package(pkg_name, meta_config)
else:
return create_versioned_deb_package(pkg_name, meta_config)
except Exception as e:
print(f"ERROR: Failed to build meta package for {pkg_name}: {e}")
return []
def build_versioned_package(pkg_name, config: PackageConfig) -> list:
"""Build versioned package (for non-gfxarch packages).
This creates a versioned package with no architecture suffix,
used for packages that don't have gfxarch variants (e.g., amdrocm-core8.2).
Parameters:
pkg_name: Name of the package to build
config: Configuration object
Returns:
List of built package filenames
"""
versioned_config = replace(config, gfx_arch="", versioned_pkg=True)
try:
if versioned_config.pkg_type == "rpm":
return create_versioned_rpm_package(pkg_name, versioned_config)
else:
return create_versioned_deb_package(pkg_name, versioned_config)
except Exception as e:
print(f"ERROR: Failed to build versioned package for {pkg_name}: {e}")
return []
def build_nonversioned_package(pkg_name, config: PackageConfig) -> list:
"""Build non-versioned package (user-facing metapackage).
This creates a non-versioned metapackage that depends on the versioned variant.
For gfxarch packages, it depends on the meta package. For non-gfxarch packages,
it depends on the versioned package (e.g., amdrocm-fft -> amdrocm-fft8.2).
IMPORTANT: Caller must set config.gfx_arch appropriately before calling:
- GFX_META for gfxarch packages in kpack mode (e.g., "amdrocm-fft")
- "" (empty string) for non-gfxarch packages in any mode (e.g., "amdrocm-core")
- Actual arch (e.g., "gfx1100") for gfxarch packages in single-arch mode (e.g., "amdrocm-fft-gfx1100")
Parameters:
pkg_name: Name of the package to build
config: Configuration object with gfx_arch already set by caller
Returns:
List of built package filenames
"""
nonversioned_config = replace(config, versioned_pkg=False)
try:
if nonversioned_config.pkg_type == "rpm":
return create_nonversioned_rpm_package(pkg_name, nonversioned_config)
else:
return create_nonversioned_deb_package(pkg_name, nonversioned_config)
except Exception as e:
print(f"ERROR: Failed to build non-versioned package for {pkg_name}: {e}")
return []
def cleanup_build_directory(config: PackageConfig):
"""Clean up build directory after all package variants are built.
This should only be called after all variants for a package are complete.
Defers cleanup to allow parallel builds of variants in the future.
Parameters:
config: Configuration object containing dest_dir and pkg_type
"""
build_dir = Path(config.dest_dir) / config.pkg_type
if build_dir.exists():
remove_dir(build_dir)
print(f"Cleaned up build directory: {build_dir}")
def cleanup_packaging_environment(config: PackageConfig):
"""Clean the packaging environment (build directories and pycache).
This is called at the start and end of the packaging run to ensure
a clean environment. Unlike cleanup_build_directory(), this also
removes Python cache files.
Parameters:
config: Configuration object containing package metadata
Returns: None
"""
print_function_name()
PYCACHE_DIR = Path(SCRIPT_DIR) / "__pycache__"
remove_dir(PYCACHE_DIR)
# NOTE: Remove only the build directory
# Make sure the destination directory is not removed
remove_dir(Path(config.dest_dir) / config.pkg_type)
# TBD:
# Currently RPATH packages are created by modifying the artifacts dir
# So artifacts dir clean up is required
# remove_dir(artifacts_dir)
def parse_input_package_list(pkg_name, artifact_dir):
"""Populate the package list from the provided input arguments.
Parameters:
pkg_name : List of packages to be created
artifact_dir: The path to the Artifactory directory
Returns: Package list
"""
print_function_name()
pkg_list = []
skipped_list = []
# If pkg_name is None, include all packages
if pkg_name is None:
pkg_list, skipped_list = get_package_list(artifact_dir)
return pkg_list, skipped_list
# Proceed if pkg_name is not None
data = read_package_json_file()
for entry in data:
# Skip if packaging is disabled
if is_packaging_disabled(entry):
continue
name = entry.get("Package")
# Loop through each type in pkg_name
for pkg in pkg_name:
if pkg == name:
pkg_list.append(name)
break
print(f"pkg_list:\n {pkg_list}")
return pkg_list, skipped_list
def create_package_config(args: argparse.Namespace) -> PackageConfig:
"""Create PackageConfig from command-line arguments.
Parses and validates input arguments to build the configuration
object used throughout the packaging process.
Parameters:
args: Parsed command-line arguments
Returns:
PackageConfig: Fully populated configuration object
Raises:
ValueError: If version string is invalid or package type is unsupported
"""
dest_dir = Path(args.dest_dir).expanduser().resolve()
# Determine target architectures
if args.target:
# Use explicitly provided targets
normalized_targets = normalize_target_list(args.target)
else:
# Auto-detect from artifact directory
normalized_targets = get_all_target_families(args.artifacts_dir)
if not normalized_targets:
print(
f"No GFX architectures found in artifact directory: {args.artifacts_dir}. "
"Either provide --target explicitly or ensure artifacts are present."
)
else:
print(f"Auto-detected GFX architectures: {normalized_targets}")
# Output packaging architecture list to GitHub Actions
github_output = os.environ.get("GITHUB_OUTPUT")
if github_output and normalized_targets:
with open(github_output, "a", encoding="utf-8") as f:
targets_str = ",".join(normalized_targets)
f.write(f"PACKAGING_ARCH_LIST={targets_str}\n")
# Auto-detect kpack from manifest if not explicitly requested via --enable-kpack
artifacts_dir = Path(args.artifacts_dir).resolve()
if not args.enable_kpack:
args.enable_kpack = load_kpack_from_manifest(artifacts_dir)
if args.enable_kpack:
print(
"Detected KPACK_SPLIT_ARTIFACTS in manifest — producing host + device packages"
)
# Configure architecture based on multi-arch mode
if args.enable_kpack:
# Multi-arch: Build host + device + meta packages for each target
# Example: amdrocm-fft-host8.2 + amdrocm-fft8.2-gfx94x + amdrocm-fft8.2 (meta)
# For non-gfxarch packages: use empty string (no arch variants, just versioned + non-versioned)
# For gfxarch packages: variant builders will set GFX_HOST, GFX_META, or device arch
default_gfx_arch = "" # Default used for non-gfxarch packages
gfxarch_list = normalized_targets
else:
# Single-arch: Build only one package for the specified target
# Example: amdrocm-fft8.2-gfx94x (no host, no other variants)
default_gfx_arch = normalized_targets[0] if normalized_targets else ""
gfxarch_list = []
# Parse version for install prefix (major.minor)
parts = args.rocm_version.split(".")
if len(parts) < 2:
raise ValueError(
f"Version string '{args.rocm_version}' does not have major.minor versions"
)
major = re.match(r"^\d+", parts[0])
minor = re.match(r"^\d+", parts[1])
modified_rocm_version = f"{major.group()}.{minor.group()}"
# Append version (and build variant for ASAN) to default install prefix.
# Release: /opt/rocm/core-7.15
# ASAN: /opt/rocm/core-asan-7.15
prefix = args.install_prefix
if prefix == DEFAULT_INSTALL_PREFIX:
if args.build_variant == "asan":
prefix = f"{prefix}-{args.build_variant}-{modified_rocm_version}"
else:
prefix = f"{prefix}-{modified_rocm_version}"
# Validate package type
pkg_type = (args.pkg_type or "").lower()
valid_types = {"deb", "rpm"}
if pkg_type not in valid_types:
raise ValueError(
f"Invalid package type: {args.pkg_type}. Must be 'deb' or 'rpm'."
)
return PackageConfig(
artifacts_dir=Path(args.artifacts_dir).resolve(),
dest_dir=dest_dir,
pkg_type=pkg_type,
rocm_version=args.rocm_version,
version_suffix=args.version_suffix,
install_prefix=prefix,
gfx_arch=default_gfx_arch,
enable_kpack=args.enable_kpack,
gfxarch_list=tuple(gfxarch_list),
build_variant=args.build_variant,
)
def run(args: argparse.Namespace):
# Create configuration from arguments
config = create_package_config(args)
# Convert RUNPATH to RPATH in artifacts before packaging (use --runpath-pkg to skip)
if not args.runpath_pkg:
print("\n=== Converting RUNPATH to RPATH in artifacts ===")
convert_runpath_to_rpath(config.artifacts_dir)
# Clean the packaging build directories
cleanup_packaging_environment(config)
pkg_list, skipped_list = parse_input_package_list(
args.pkg_names, config.artifacts_dir
)
if not pkg_list:
print("Error: No packages found to build. Package list is empty.")
sys.exit(1)
current_pkg_idx = 0
try:
built_pkglist = []
failed_pkglist = []
for current_pkg_idx, pkg_name in enumerate(pkg_list):
print(f"Creating {config.pkg_type} package: {pkg_name}")
# Build all package variants for this package
output_list = build_package_variants(pkg_name, config)
if output_list:
built_pkglist.extend(output_list)
print(
f"\n✓ Successfully built {len(output_list)} variant(s) for {pkg_name}"
)
else:
# Package failed to build
failed_pkglist.append(pkg_name)
print(f"\n✗ Failed to build any variants for {pkg_name}")
# Clean the build directories
cleanup_packaging_environment(config)
if built_pkglist:
print(f"\nBuilt packages: {built_pkglist}")
pkglist_status = PackageList(
total=pkg_list,
built=built_pkglist,
skipped=skipped_list,
failed=failed_pkglist,
)
# Print build summary
print_build_summary(config, pkglist_status)
except SystemExit as e:
# Build aborted somewhere inside create_* functions
tb = traceback.extract_tb(sys.exc_info()[2])
if tb:
filename, line_no, func, text = tb[-1]
print(f"\n❌ Build aborted due to an error at {filename}:{line_no}: {e}\n")
else:
print(f"\n❌ Build aborted due to an error: {e}\n")
# Record failed package and all pending packages
failed_pkglist.append(pkg_list[current_pkg_idx])
pending_pkgs = pkg_list[current_pkg_idx + 1 :]
failed_pkglist.extend(pending_pkgs)
pkglist_status = PackageList(
total=pkg_list,
built=built_pkglist,
skipped=skipped_list,
failed=failed_pkglist,
)
print_build_summary(config, pkglist_status)
# Stop the program
raise
def main(argv: list[str]):
p = argparse.ArgumentParser()
p.add_argument(
"--artifacts-dir",
type=Path,
required=True,
help="Specify the directory for source artifacts",
)
p.add_argument(
"--dest-dir",
type=Path,
required=True,
help="Destination directory where the packages will be materialized",
)
p.add_argument(
"--target",
type=str,
nargs="+",
required=False,
help="Graphics architecture(s) used for the artifacts. "
"Multiple targets can be specified space-separated, comma-separated, or semicolon-separated "
"(e.g., 'gfx1100 gfx1101', 'gfx1100,gfx1101', or 'gfx1100;gfx1101'). "
"If not provided, will auto-detect from artifact directory.",
)
p.add_argument(
"--pkg-type",
type=str,
required=True,
help="Choose the package format to be generated: DEB or RPM",
)
p.add_argument(
"--rocm-version", type=str, required=True, help="ROCm Release version"
)
p.add_argument(
"--version-suffix",
type=str,
nargs="?",
help=(
"Release identifier appended to the package version field in DEB/RPM "
"metadata (e.g. a CI run ID like '28484694006'). "
"For DEB this becomes the debian revision (e.g. '7.15.0-28484694006'); "
"for RPM this sets the release field. "
"Does not affect the package name or install prefix."
),
)
p.add_argument(
"--install-prefix",
default=f"{DEFAULT_INSTALL_PREFIX}",
help="Base directory where package will be installed",
)
p.add_argument(
"--build-variant",
default="",
help=(
"Build variant (e.g. 'asan'). When set to 'asan', the install prefix "
"becomes DEFAULT_INSTALL_PREFIX-asan-MAJOR.MINOR "
"(e.g. /opt/rocm/core-asan-7.15)."
),
)
p.add_argument(
"--runpath-pkg",
action="store_true",
help="Keep RUNPATH in binaries (by default, RUNPATH is converted to RPATH)",
)
p.add_argument(
"--enable-kpack",
action="store_true",
help="Enable multi-architecture package generation",
)
p.add_argument(
"--clean-build",
action="store_true",
help="Clean the packaging environment",
)
p.add_argument(
"--pkg-names",
nargs="+",
help="Specify the packages to be created",
)
args = p.parse_args(argv)
run(args)
if __name__ == "__main__":
main(sys.argv[1:])