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
214 lines
7.5 KiB
Python
Executable File
214 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Exports a canonical source archive from a pristine git worktree.
|
|
|
|
Many downstream packagers operate off of source archives, and this utility exports
|
|
canonical source archives of different types for such consumption.
|
|
|
|
There are two types of source archives:
|
|
|
|
* Pure sources: These archives contain all source material needed to build the project.
|
|
They contain a recursive export of all contained submodules that have been
|
|
initialized. In the future, as well, they will optionally contain any files that
|
|
may normally be fetched lazily as part of the build (i.e. third party dependency
|
|
archives, pre-generated bundles, etc) so that the resulting package is self
|
|
contained and completely sufficient to build.
|
|
|
|
* Prebuilt archives: These archives contain only an export of the super-project git
|
|
repository but no source project submodules or build dependency bundles. These
|
|
archives are used for cross generating various downstream packages without executing
|
|
their own build step. These archives have a number of uses:
|
|
|
|
* Generating DEB or RPM packages without a build step, using build artifacts from
|
|
some other source (typically built on a neutral, old glibc version that is
|
|
widely portable with many operating systems).
|
|
* Generating binary installers (i.e. NSIS, etc) without actually performing a
|
|
built.
|
|
|
|
There are several special files and locations in export archives:
|
|
|
|
* `GIT_REVISION`: Text file containing the HEAD commit hash at the time of export.
|
|
* `GIT_ORIGIN`: Text file containing the origin URL at the time of export (or empty).
|
|
* `PREBUILT`: Marker file, indicating that if present, prebuilt artifacts are present
|
|
in the `build/artifacts` directory as the output of some prior, offline build
|
|
process.
|
|
|
|
These files are standard locations and the build system may be hard-wired to incorporate
|
|
them in preference to other mechanisms of obtaining this information.
|
|
|
|
While not yet implemented, in the future, the cache of downloaded build deps will be
|
|
stored in the `.build_dep_cache/` with file names keyed by a `BUILD_DEP_ID` in the build
|
|
system. The build system will automatically use these if available.
|
|
"""
|
|
|
|
from abc import ABC, abstractmethod
|
|
import argparse
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tarfile
|
|
import tempfile
|
|
|
|
from _therock_utils.pattern_match import PatternMatcher
|
|
|
|
REPO_DIR = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
class ArchiveWriter(ABC):
|
|
@staticmethod
|
|
def create(p: Path, compresslevel: int):
|
|
name = p.name
|
|
if name.endswith(".tar"):
|
|
return TarArchiveWriter(
|
|
tarfile.open(p, mode="w", compresslevel=compresslevel)
|
|
)
|
|
elif name.endswith(".tar.gz") or name.endswith(".tgz"):
|
|
return TarArchiveWriter(
|
|
tarfile.open(p, mode="w:gz", compresslevel=compresslevel)
|
|
)
|
|
elif name.endswith(".tar.bz2"):
|
|
return TarArchiveWriter(
|
|
tarfile.open(p, mode="w:bz2", compresslevel=compresslevel)
|
|
)
|
|
elif name.endswith(".tar.xz"):
|
|
return TarArchiveWriter(
|
|
tarfile.open(p, mode="w:xz", compresslevel=compresslevel)
|
|
)
|
|
else:
|
|
raise ValueError(f"Unsupported archive file extension for: {name}")
|
|
|
|
@abstractmethod
|
|
def close(self): ...
|
|
|
|
@abstractmethod
|
|
def add_file(self, path: Path, arcname: str): ...
|
|
|
|
@abstractmethod
|
|
def add_directory(self, path: Path, arcname: str): ...
|
|
|
|
@abstractmethod
|
|
def add_text(self, text: str, arcname: str): ...
|
|
|
|
|
|
class TarArchiveWriter(ArchiveWriter):
|
|
def __init__(self, archive: tarfile.TarFile):
|
|
self.archive = archive
|
|
|
|
def close(self):
|
|
self.archive.close()
|
|
|
|
def add_file(self, path: Path, arcname: str):
|
|
self.archive.add(str(path), arcname, recursive=False)
|
|
|
|
def add_directory(self, path: Path, arcname: str):
|
|
self.archive.add(str(path), arcname, recursive=True)
|
|
|
|
def add_text(self, text: str, arcname: str):
|
|
with tempfile.NamedTemporaryFile(mode="wt") as tf:
|
|
tf.write(text)
|
|
self.archive.add(Path(tf.name), arcname)
|
|
|
|
|
|
def git_ls_files(repo_dir: Path, recurse_submodules: bool = False):
|
|
cl = ["git", "ls-files"]
|
|
if recurse_submodules:
|
|
cl += ["--recurse-submodules"]
|
|
cl += [str(repo_dir)]
|
|
lines = subprocess.check_output(cl).decode()
|
|
return lines.splitlines()
|
|
|
|
|
|
def git_head_revision(repo_dir: Path) -> str:
|
|
cl = ["git", "rev-parse", "HEAD"]
|
|
return subprocess.check_output(cl).decode().strip()
|
|
|
|
|
|
def git_origin(repo_dir: Path) -> str:
|
|
cl = ["git", "remote", "get-url", "origin"]
|
|
try:
|
|
return subprocess.check_output(cl).decode().strip()
|
|
except subprocess.CalledProcessError:
|
|
# It is legal for there to be no origin.
|
|
return ""
|
|
|
|
|
|
def progress_iter(iterable, desc: str | None):
|
|
try:
|
|
import tqdm
|
|
except ImportError:
|
|
return iterable
|
|
return tqdm.tqdm(
|
|
iterable,
|
|
desc=desc,
|
|
unit="file",
|
|
bar_format="{l_bar}|{bar}| {n_fmt}/{total_fmt}{postfix}",
|
|
)
|
|
|
|
|
|
def create_archive(
|
|
writer: ArchiveWriter, source_dir: Path, prebuilt_artifacts_dir: Path | None
|
|
):
|
|
# Write some metadata.
|
|
head_revision = git_head_revision(source_dir)
|
|
origin = git_origin(source_dir)
|
|
writer.add_text(head_revision, "GIT_REVISION")
|
|
writer.add_text(origin, "GIT_ORIGIN")
|
|
|
|
# Write a marker file to indicate that the archive is a PREBUILT archive.
|
|
# Uses the relative path to the artifacts dir as the content, but this
|
|
# is also assumed fixed (just written here for posterity).
|
|
if prebuilt_artifacts_dir is not None:
|
|
writer.add_text("build/artifacts", "PREBUILT")
|
|
|
|
# Get the source files. If generating a pre-built archive, we do not
|
|
# recurse submodules. Otherwise, we do.
|
|
source_files = git_ls_files(
|
|
source_dir, recurse_submodules=prebuilt_artifacts_dir is None
|
|
)
|
|
for source_file in progress_iter(source_files, desc="Adding source files"):
|
|
source_abs_path = source_dir / source_file
|
|
writer.add_file(source_abs_path, source_file)
|
|
|
|
if prebuilt_artifacts_dir:
|
|
pm = PatternMatcher()
|
|
pm.add_basedir(prebuilt_artifacts_dir)
|
|
artifact_files = list(pm.matches())
|
|
for relpath, direntry in progress_iter(
|
|
artifact_files, desc="Adding prebuilt files"
|
|
):
|
|
# We canonically organize source tarballs to have artifacts in
|
|
# build/artifacts, no matter where they came from.
|
|
writer.add_file(Path(direntry.path), f"build/artifacts/{relpath}")
|
|
|
|
|
|
def main(argv: list[str]):
|
|
p = argparse.ArgumentParser("create_source_tarball")
|
|
p.add_argument(
|
|
"-s",
|
|
"--source-dir",
|
|
type=Path,
|
|
default=REPO_DIR,
|
|
help="Path to TheRock source directory",
|
|
)
|
|
p.add_argument(
|
|
"--prebuilt-artifacts",
|
|
type=Path,
|
|
help="If specified, creates a tarball which contains pre-built artifacts vs sources (prebuilt)",
|
|
)
|
|
p.add_argument("-o", "--output", type=Path, required=True, help="Output tarball")
|
|
p.add_argument(
|
|
"--compress-level", type=int, default=4, help="Tar compression level"
|
|
)
|
|
args = p.parse_args(argv)
|
|
writer = ArchiveWriter.create(args.output, compresslevel=args.compress_level)
|
|
try:
|
|
create_archive(writer, args.source_dir, args.prebuilt_artifacts)
|
|
finally:
|
|
writer.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1:])
|