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
313 lines
11 KiB
Python
313 lines
11 KiB
Python
#!/usr/bin/env python
|
|
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""fileset_tool.py
|
|
|
|
Helper tool for manipulating filesets by listing matching files, copying,
|
|
archiving, etc. This is ultimately inspired by the fileset manipulation behavior
|
|
of Ant, which uses recursive glob include/exclude patterns rooted on some
|
|
base directory to manage artifact moving and packaging.
|
|
|
|
This is based on a limited form of the pathlib.Path pattern language introduced
|
|
in Python 3.13 (https://docs.python.org/3/library/pathlib.html#pattern-language)
|
|
with the following changes:
|
|
|
|
* It does not support character classes.
|
|
"""
|
|
|
|
from typing import Callable
|
|
import argparse
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
from _therock_utils.archive_util import open_archive_for_write
|
|
from _therock_utils.artifacts import ArtifactPopulator
|
|
import _therock_utils.artifact_builder as artifact_builder
|
|
from _therock_utils.hash_util import calculate_hash, write_hash
|
|
from _therock_utils.os_util import rmtree_with_retry
|
|
from _therock_utils.pattern_match import PatternMatcher
|
|
|
|
|
|
def do_list(args: argparse.Namespace, pm: PatternMatcher):
|
|
for relpath, direntry in pm.matches():
|
|
print(relpath)
|
|
|
|
|
|
def do_copy(args: argparse.Namespace, pm: PatternMatcher):
|
|
verbose = args.verbose
|
|
destdir: Path = args.dest_dir
|
|
pm.copy_to(
|
|
destdir=destdir,
|
|
verbose=verbose,
|
|
always_copy=args.always_copy,
|
|
remove_dest=args.remove_dest,
|
|
)
|
|
|
|
|
|
def do_artifact(args):
|
|
"""Produces an 'artifact directory', which is a slice of installed stage/
|
|
directories, split into components (i.e. run, dev, dbg, doc, test).
|
|
"""
|
|
descriptor = artifact_builder.ArtifactDescriptor.load_toml_file(
|
|
args.descriptor, artifact_name=args.artifact_name
|
|
)
|
|
scanner = artifact_builder.ComponentScanner(args.root_dir, descriptor)
|
|
# Disable strict verification temporarily until debug builds are tested/fixed.
|
|
# scanner.verify()
|
|
component_dirs = args.component_dirs
|
|
# It is an alternating list of <component> <dir> so must be divisible by 2.
|
|
if len(component_dirs) % 2:
|
|
raise SystemExit(
|
|
"Expected component dirs to be alternating list of component names and directories"
|
|
)
|
|
|
|
for i in range(len(component_dirs) // 2):
|
|
component_name = component_dirs[i * 2]
|
|
output_dir = Path(component_dirs[i * 2 + 1])
|
|
|
|
# Setup output dir.
|
|
if output_dir.exists():
|
|
rmtree_with_retry(output_dir)
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
try:
|
|
contents = scanner.components[component_name]
|
|
except KeyError:
|
|
return
|
|
contents.write_artifact(output_dir)
|
|
|
|
|
|
def do_artifact_archive(args):
|
|
output_path: Path = args.o
|
|
if output_path.exists():
|
|
output_path.unlink()
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
with open_archive_for_write(
|
|
output_path, args.compression_type, args.compression_level
|
|
) as arc:
|
|
for artifact_path in args.artifact:
|
|
manifest_path: Path = artifact_path / "artifact_manifest.txt"
|
|
relpaths = manifest_path.read_text().splitlines()
|
|
# Important: The manifest must be stored first.
|
|
arc.add(manifest_path, arcname=manifest_path.name, recursive=False)
|
|
for relpath in relpaths:
|
|
if not relpath:
|
|
continue
|
|
source_dir = artifact_path / relpath
|
|
if not source_dir.exists():
|
|
continue
|
|
pm = PatternMatcher()
|
|
pm.add_basedir(source_dir)
|
|
for subpath, dir_entry in pm.all.items():
|
|
fullpath = f"{relpath}/{subpath}"
|
|
arc.add(dir_entry.path, arcname=fullpath, recursive=False)
|
|
|
|
if args.hash_file:
|
|
digest = calculate_hash(output_path, args.hash_algorithm)
|
|
write_hash(args.hash_file, digest)
|
|
|
|
|
|
def _do_artifact_flatten(args):
|
|
flattener = ArtifactPopulator(
|
|
output_path=args.o, verbose=args.verbose, flatten=True
|
|
)
|
|
flattener(*args.artifact)
|
|
relpaths = list(flattener.relpaths)
|
|
relpaths.sort()
|
|
if args.verbose:
|
|
for relpath in relpaths:
|
|
print(relpath)
|
|
|
|
|
|
def _do_artifact_flatten_split(args):
|
|
"""Flatten split artifacts discovered by globbing at build time.
|
|
|
|
Split artifacts produce directories like:
|
|
artifacts/miopen_lib_generic/
|
|
artifacts/miopen_lib_gfx1201/
|
|
The exact per-target names can't be predicted at CMake configure time
|
|
(xnack suffixes, clang defaults), so we discover them by globbing.
|
|
"""
|
|
artifacts_dir: Path = args.artifacts_dir
|
|
discovered_dirs: list[Path] = []
|
|
for prefix in args.prefixes:
|
|
for candidate in sorted(artifacts_dir.glob(f"{prefix}_*")):
|
|
if candidate.is_dir() and (candidate / "artifact_manifest.txt").exists():
|
|
discovered_dirs.append(candidate)
|
|
if not discovered_dirs:
|
|
print(
|
|
f"Warning: no split artifact dirs found in {artifacts_dir} "
|
|
f"for prefixes: {args.prefixes}"
|
|
)
|
|
return
|
|
if args.verbose:
|
|
print(f"Discovered {len(discovered_dirs)} split artifact dirs:")
|
|
for d in discovered_dirs:
|
|
print(f" {d.name}")
|
|
flattener = ArtifactPopulator(
|
|
output_path=args.o, verbose=args.verbose, flatten=True
|
|
)
|
|
flattener(*discovered_dirs)
|
|
relpaths = list(flattener.relpaths)
|
|
relpaths.sort()
|
|
if args.verbose:
|
|
for relpath in relpaths:
|
|
print(relpath)
|
|
|
|
|
|
def main(cl_args: list[str]):
|
|
def add_pattern_matcher_args(p: argparse.ArgumentParser):
|
|
p.add_argument("basedir", type=Path, nargs="*", help="Base directories to scan")
|
|
p.add_argument("--include", nargs="+", help="Recursive glob pattern to include")
|
|
p.add_argument("--exclude", nargs="+", help="Recursive glob pattern to exclude")
|
|
p.add_argument("--verbose", action="store_true", help="Print verbose status")
|
|
|
|
def pattern_matcher_action(
|
|
action: Callable[[argparse.Namespace, PatternMatcher], None],
|
|
):
|
|
def run_action(args: argparse.Namespace):
|
|
if not args.basedir:
|
|
# base dir is CWD
|
|
args.basedir = [Path.cwd()]
|
|
pm = PatternMatcher(args.include or [], args.exclude or [])
|
|
for basedir in args.basedir:
|
|
pm.add_basedir(basedir)
|
|
action(args, pm)
|
|
|
|
return run_action
|
|
|
|
p = argparse.ArgumentParser(
|
|
"fileset_tool.py", usage="fileset_tool.py {command} ..."
|
|
)
|
|
sub_p = p.add_subparsers(required=True)
|
|
# 'copy' command
|
|
copy_p = sub_p.add_parser("copy", help="Copy matching files to a destination dir")
|
|
copy_p.add_argument("dest_dir", type=Path, help="Destination directory")
|
|
copy_p.add_argument(
|
|
"--always-copy", action="store_true", help="Always copy vs attempting to link"
|
|
)
|
|
copy_p.add_argument(
|
|
"--remove-dest",
|
|
default=True,
|
|
action=argparse.BooleanOptionalAction,
|
|
help="Remove the destination directory before copying",
|
|
)
|
|
add_pattern_matcher_args(copy_p)
|
|
copy_p.set_defaults(func=pattern_matcher_action(do_copy))
|
|
|
|
# 'list' command
|
|
list_p = sub_p.add_parser("list", help="List matching files to stdout")
|
|
add_pattern_matcher_args(list_p)
|
|
list_p.set_defaults(func=pattern_matcher_action(do_list))
|
|
|
|
# 'artifact' command
|
|
artifact_p = sub_p.add_parser(
|
|
"artifact", help="Merge artifacts based on a descriptor"
|
|
)
|
|
artifact_p.add_argument(
|
|
"--root-dir",
|
|
type=Path,
|
|
required=True,
|
|
help="Source directory to which all descriptor directories are relative",
|
|
)
|
|
artifact_p.add_argument(
|
|
"--descriptor",
|
|
type=Path,
|
|
required=True,
|
|
help="TOML file describing the artifact",
|
|
)
|
|
artifact_p.add_argument(
|
|
"--artifact-name",
|
|
type=str,
|
|
required=True,
|
|
help="Name of the artifact (e.g., rccl, blas) for kpack pattern matching",
|
|
)
|
|
artifact_p.add_argument(
|
|
"component_dirs",
|
|
nargs="+",
|
|
help="Alternating list of component name and directory to write it to",
|
|
)
|
|
artifact_p.set_defaults(func=do_artifact)
|
|
|
|
# 'artifact-archive' command
|
|
artifact_archive_p = sub_p.add_parser(
|
|
"artifact-archive",
|
|
help="Creates an archive file from one or more artifact directories",
|
|
)
|
|
artifact_archive_p.add_argument(
|
|
"artifact", nargs="+", type=Path, help="Artifact directory"
|
|
)
|
|
artifact_archive_p.add_argument(
|
|
"-o", type=Path, required=True, help="Output archive name"
|
|
)
|
|
artifact_archive_p.add_argument(
|
|
"--compression-type",
|
|
choices=["zstd", "xz"],
|
|
default="xz",
|
|
help="Compression algorithm (default: xz)",
|
|
)
|
|
artifact_archive_p.add_argument(
|
|
"--compression-level",
|
|
type=int,
|
|
default=None,
|
|
help="Compression level (default: 3 for zstd, 6 for xz)",
|
|
)
|
|
artifact_archive_p.add_argument(
|
|
"--hash-file",
|
|
type=Path,
|
|
help="Hash file to write representing the archive contents",
|
|
)
|
|
artifact_archive_p.add_argument(
|
|
"--hash-algorithm", default="sha256", help="Hash algorithm"
|
|
)
|
|
artifact_archive_p.set_defaults(func=do_artifact_archive)
|
|
|
|
# 'artifact-flatten' command
|
|
artifact_flatten_p = sub_p.add_parser(
|
|
"artifact-flatten",
|
|
help="Flattens one or more artifact directories into one output directory",
|
|
)
|
|
artifact_flatten_p.add_argument(
|
|
"artifact", nargs="+", type=Path, help="Artifact directory"
|
|
)
|
|
artifact_flatten_p.add_argument(
|
|
"-o", type=Path, required=True, help="Output archive name"
|
|
)
|
|
artifact_flatten_p.add_argument(
|
|
"--verbose", action="store_true", help="Print verbose status"
|
|
)
|
|
artifact_flatten_p.set_defaults(func=_do_artifact_flatten)
|
|
|
|
# 'artifact-flatten-split' command
|
|
artifact_flatten_split_p = sub_p.add_parser(
|
|
"artifact-flatten-split",
|
|
help="Flatten split artifact directories discovered by globbing",
|
|
)
|
|
artifact_flatten_split_p.add_argument(
|
|
"prefixes",
|
|
nargs="+",
|
|
help="Artifact prefixes to match (e.g., miopen_lib miopen_dev)",
|
|
)
|
|
artifact_flatten_split_p.add_argument(
|
|
"-o", type=Path, required=True, help="Output directory"
|
|
)
|
|
artifact_flatten_split_p.add_argument(
|
|
"--artifacts-dir",
|
|
type=Path,
|
|
required=True,
|
|
help="Base directory containing split artifact dirs",
|
|
)
|
|
artifact_flatten_split_p.add_argument(
|
|
"--verbose", action="store_true", help="Print verbose status"
|
|
)
|
|
artifact_flatten_split_p.set_defaults(func=_do_artifact_flatten_split)
|
|
|
|
args = p.parse_args(cl_args)
|
|
args.func(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1:])
|