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
268 lines
9.2 KiB
Python
268 lines
9.2 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""RPM package creation functions for ROCm packaging."""
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from dataclasses import replace
|
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
from pathlib import Path
|
|
|
|
from packaging_utils import *
|
|
|
|
# Setup paths
|
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
|
|
|
|
def create_nonversioned_rpm_package(pkg_name, config: PackageConfig):
|
|
"""Create a non-versioned RPM meta package (.rpm).
|
|
|
|
Builds a minimal RPM binary package whose payload is empty and whose primary
|
|
purpose is to express dependencies. The package name does not embed a version
|
|
|
|
Parameters:
|
|
pkg_name : Name of the package to be created
|
|
config: Configuration object containing package metadata
|
|
|
|
Returns:
|
|
output_list: List of packages created
|
|
"""
|
|
print_function_name()
|
|
# Create immutable config copy with versioned_pkg=False
|
|
build_config = replace(config, versioned_pkg=False)
|
|
|
|
# Use updated package name for build directory to avoid collisions between variants
|
|
updated_pkg_name = update_package_name(pkg_name, build_config)
|
|
package_dir = Path(build_config.dest_dir) / build_config.pkg_type / updated_pkg_name
|
|
specfile = package_dir / "specfile"
|
|
generate_spec_file(pkg_name, specfile, build_config)
|
|
package_with_rpmbuild(specfile)
|
|
|
|
# Move packages to destination
|
|
output_list = move_packages_to_destination(updated_pkg_name, build_config)
|
|
return output_list
|
|
|
|
|
|
def create_versioned_rpm_package(pkg_name, config: PackageConfig):
|
|
"""Create a versioned RPM package (.rpm).
|
|
|
|
This function automates the process of building a RPM package by:
|
|
1) Generating the spec file with appropriate fields (Package,
|
|
Version, Architecture, Maintainer, Description, and dependencies).
|
|
2) Invoking `rpmbuild` to assemble the final `.rpm` file.
|
|
|
|
Parameters:
|
|
pkg_name : Name of the package to be created
|
|
config: Configuration object containing package metadata
|
|
|
|
Returns:
|
|
output_list: List of packages created
|
|
"""
|
|
print_function_name()
|
|
# Explicitly ensure versioned_pkg=True
|
|
build_config = replace(config, versioned_pkg=True)
|
|
|
|
# Use updated package name for build directory to avoid collisions between variants
|
|
# Each variant (host, device, meta) gets its own build directory
|
|
updated_pkg_name = update_package_name(pkg_name, build_config)
|
|
package_dir = Path(build_config.dest_dir) / build_config.pkg_type / updated_pkg_name
|
|
specfile = package_dir / "specfile"
|
|
generate_spec_file(pkg_name, specfile, build_config)
|
|
package_with_rpmbuild(specfile)
|
|
|
|
# Move packages to destination
|
|
output_list = move_packages_to_destination(updated_pkg_name, build_config)
|
|
return output_list
|
|
|
|
|
|
def generate_spec_file(pkg_name, specfile, config: PackageConfig):
|
|
"""Generate an RPM spec file.
|
|
|
|
Parameters:
|
|
pkg_name : Package name
|
|
specfile: Path where the generated spec file should be saved
|
|
config: Configuration object containing package metadata
|
|
|
|
Returns: None
|
|
"""
|
|
print_function_name()
|
|
os.makedirs(os.path.dirname(specfile), exist_ok=True)
|
|
|
|
pkg_info = get_package_info(pkg_name) # Raises ValueError if not found
|
|
version = f"{config.rocm_version}"
|
|
is_meta = is_meta_package(pkg_info)
|
|
|
|
# Initialize optional fields
|
|
provides = obsoletes = conflicts = ""
|
|
rpmrecommends = rpmsuggests = ""
|
|
sourcedir_list = []
|
|
rpm_scripts = []
|
|
# amdrocm-debugger: Exclude libpython requirements
|
|
# Multiple Python-version-specific binaries are included; the wrapper script
|
|
# automatically selects the binary matching the system's Python version
|
|
exclude_libpython_requires = pkg_name == "amdrocm-debugger"
|
|
|
|
if config.versioned_pkg:
|
|
# Get -> Filter -> Transform
|
|
rpmrecommends = process_secondary_dependencies(
|
|
pkg_info, "RPMRecommends", config
|
|
)
|
|
rpmsuggests = process_secondary_dependencies(pkg_info, "RPMSuggests", config)
|
|
requires = process_main_dependencies(pkg_info, "RPMRequires", config)
|
|
|
|
dir_list = filter_components_fromartifactory(
|
|
pkg_name, config.artifacts_dir, config.gfx_arch, config.enable_kpack
|
|
)
|
|
sourcedir_list.extend(dir_list)
|
|
|
|
# Filter out non-existing directories
|
|
sourcedir_list = [path for path in sourcedir_list if os.path.isdir(path)]
|
|
|
|
# GFX_META is a versioned meta package (empty content, just dependencies)
|
|
is_gfx_meta = config.enable_kpack and config.gfx_arch == GFX_META
|
|
|
|
# Warn if we have no artifacts for non-meta packages
|
|
if not sourcedir_list and not is_meta and not is_gfx_meta:
|
|
if config.enable_kpack:
|
|
print(
|
|
f"WARNING: {pkg_name}: Empty sourcedir_list and not a meta package, creating empty RPM"
|
|
)
|
|
else:
|
|
sys.exit(
|
|
f"{pkg_name}: Empty sourcedir_list and not a meta package, exiting"
|
|
)
|
|
|
|
if is_postinstallscripts_available(pkg_info):
|
|
rpm_scripts = generate_rpm_postscripts(pkg_info, config)
|
|
|
|
else:
|
|
# Get -> Transform -> Join (no transform needed for RPM)
|
|
provides = process_name_field(pkg_info, "Provides")
|
|
obsoletes = process_name_field(pkg_info, "Obsoletes")
|
|
conflicts = process_name_field(pkg_info, "Conflicts")
|
|
# Non-versioned package requires versioned package itself
|
|
requires = resolve_versioned_dependencies([pkg_name], config, is_meta)
|
|
|
|
pkg_name = update_package_name(pkg_name, config)
|
|
|
|
env = Environment(
|
|
loader=FileSystemLoader(str(SCRIPT_DIR)),
|
|
autoescape=select_autoescape(
|
|
enabled_extensions=("html", "htm", "xml"),
|
|
default_for_string=True,
|
|
default=False,
|
|
),
|
|
)
|
|
template = env.get_template("template/rpm_specfile.j2")
|
|
context = {
|
|
"pkg_name": pkg_name,
|
|
"version": version,
|
|
"release": config.version_suffix,
|
|
"build_arch": pkg_info.get("BuildArch"),
|
|
"description_short": pkg_info.get("Description_Short"),
|
|
"description_long": pkg_info.get("Description_Long"),
|
|
"group": pkg_info.get("Group"),
|
|
"pkg_license": pkg_info.get("License"),
|
|
"vendor": pkg_info.get("Vendor"),
|
|
"install_prefix": config.install_prefix,
|
|
"requires": requires,
|
|
"provides": provides,
|
|
"obsoletes": obsoletes,
|
|
"conflicts": conflicts,
|
|
"rpmrecommends": rpmrecommends,
|
|
"rpmsuggests": rpmsuggests,
|
|
"disable_rpm_strip": True,
|
|
"disable_debug_package": is_debug_package_disabled(pkg_info),
|
|
"sourcedir_list": sourcedir_list,
|
|
"rpm_scripts": rpm_scripts,
|
|
"exclude_libpython_requires": exclude_libpython_requires,
|
|
}
|
|
|
|
with open(specfile, "w", encoding="utf-8") as f:
|
|
f.write(template.render(context))
|
|
|
|
|
|
def generate_rpm_postscripts(pkg_info, config: PackageConfig):
|
|
"""Generate RPM postinst/prerm sections.
|
|
|
|
Parameters:
|
|
pkg_info: Package details parsed from a JSON file
|
|
config: Configuration object containing package metadata
|
|
|
|
Returns: rpm script sections for specfile
|
|
"""
|
|
# RPM maintainer scripts
|
|
EXEC_SCRIPTS = {
|
|
"preinst": "%pre",
|
|
"postinst": "%post",
|
|
"prerm": "%preun",
|
|
"postrm": "%postun",
|
|
}
|
|
pkg_name = pkg_info.get("Package")
|
|
parts = config.rocm_version.split(".")
|
|
env = Environment(
|
|
loader=FileSystemLoader(str(SCRIPT_DIR)),
|
|
autoescape=select_autoescape(
|
|
enabled_extensions=("html", "htm", "xml"),
|
|
default_for_string=True,
|
|
default=False,
|
|
),
|
|
)
|
|
# Prepare your context dictionary
|
|
context = {
|
|
"install_prefix": config.install_prefix,
|
|
"version_major": int(re.match(r"^\d+", parts[0]).group()),
|
|
"version_minor": int(re.match(r"^\d+", parts[1]).group()),
|
|
"version_patch": int(re.match(r"^\d+", parts[2]).group()),
|
|
"target": "rpm",
|
|
}
|
|
|
|
templates_root = Path(SCRIPT_DIR) / "template" / "scripts"
|
|
# Collect all matching files
|
|
# This will hold rendered RPM script sections
|
|
rpm_script_sections = {}
|
|
|
|
for script, rpm_section in EXEC_SCRIPTS.items():
|
|
pattern = f"{pkg_name}-{script}.j2"
|
|
|
|
for file in templates_root.glob(pattern):
|
|
template = env.get_template(str(file.relative_to(SCRIPT_DIR)))
|
|
rendered = template.render(context)
|
|
|
|
# Store rendered script under its RPM section name
|
|
rpm_script_sections[rpm_section] = rendered
|
|
|
|
return rpm_script_sections
|
|
|
|
|
|
def package_with_rpmbuild(spec_file):
|
|
"""Generate a RPM package using `rpmbuild`
|
|
|
|
Parameters:
|
|
spec_file: Path to the RPM spec file
|
|
|
|
Returns: None
|
|
"""
|
|
print_function_name()
|
|
# Build the command
|
|
cmd = [
|
|
"rpmbuild",
|
|
"-bb",
|
|
spec_file,
|
|
"--define",
|
|
f"_topdir {spec_file.parent}",
|
|
]
|
|
|
|
# Execute the command
|
|
try:
|
|
subprocess.run(cmd, check=True)
|
|
print(f"RPM Package built successfully: {spec_file.name}")
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Error building RPM package: {spec_file.name}: {e}")
|
|
sys.exit(e.returncode)
|