Files
TheRock/rocm-libraries/projects/hipblaslt/tools/scripts/validate_library_layout.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

226 lines
7.1 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""Validate the installed hipBLASLt library tree against the per-base layout."""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
from typing import List, Optional, Set
REQUIRED_PER_BASE_FILES = (
"hipblasltTransform_{arch}.hsaco",
"hipblasltExtOpLibrary_{arch}.dat",
"extop_{arch}.co",
)
TENSILE_MASTER_CANDIDATES = (
"TensileLibrary_{arch}.dat",
"TensileLibrary_{arch}.dat.zlib",
"TensileLibrary_{arch}.yaml",
)
TENSILE_LAZY_CANDIDATES = (
"TensileLibrary_lazy_{arch}.dat",
"TensileLibrary_lazy_{arch}.dat.zlib",
"TensileLibrary_lazy_{arch}.yaml",
)
PER_ARCH_REQUIRED = {
"gfx950": ("rr_custom_kernels_gfx950.co",),
}
FORBIDDEN_FLAT_ROOT_BASENAMES = (
"TensileLibrary.dat",
"TensileLibrary.dat.zlib",
"TensileLibrary.yaml",
"TensileLibrary_lazy.dat",
"TensileLibrary_lazy.dat.zlib",
"TensileLibrary_lazy.yaml",
"hipblasltTransform.hsaco",
"hipblasltExtOpLibrary.dat",
"hipblasltExtOpLibrary.dat.zlib",
"extop.co",
)
_GFX_PREFIX_RE = re.compile(r"^gfx[a-z0-9]+(?:[\-:][\-+a-z0-9]+)*$")
def _arch_dir_name_is_base(name: str) -> bool:
return bool(re.fullmatch(r"gfx[a-z0-9]+", name))
def _filename_arch_matches_dir(filename: str, base_arch: str) -> bool:
pattern = re.compile(r"(?:^|[._-])(?P<arch>gfx[0-9a-z]+(?:[-+][0-9a-z]+)*)")
for m in pattern.finditer(filename):
found = m.group("arch")
if (
found == base_arch
or found.startswith(base_arch + "-")
or found.startswith(base_arch + "+")
):
return True
return False
def _library_root(install_root: Path) -> Optional[Path]:
candidates = (
install_root / "lib" / "hipblaslt" / "library",
install_root / "library",
)
for c in candidates:
if c.is_dir():
return c
return None
def _has_required_file(entries: Set[str], template: str, arch: str) -> bool:
"""Check if entries contains the required file or its .zlib variant (for .dat only)."""
wanted = template.format(arch=arch)
if wanted in entries:
return True
if wanted.endswith(".dat"):
return (wanted + ".zlib") in entries
return False
def validate(install_root: Path) -> List[str]:
install_root = Path(install_root).resolve()
violations: List[str] = []
if not install_root.is_dir():
return [f"install_root does not exist or is not a directory: {install_root}"]
library_dir = _library_root(install_root)
if library_dir is None:
return [
f"library dir not found under {install_root}; expected "
f"<root>/lib/hipblaslt/library/ or <root>/library/"
]
for basename in FORBIDDEN_FLAT_ROOT_BASENAMES:
offender = library_dir / basename
if offender.is_file():
violations.append(
f"flat-root file found (per-base layout violation): {offender}"
)
for entry in library_dir.iterdir():
if entry.is_file() and entry.suffix in (".dat", ".zlib", ".co", ".hsaco", ".yaml"):
violations.append(
f"unexpected payload file at library root: {entry} "
f"(per-base layout requires files in library/<base>/)"
)
base_arch_dirs = sorted(
p for p in library_dir.iterdir() if p.is_dir() and p.name.startswith("gfx")
)
if not base_arch_dirs:
violations.append(f"no per-base gfx* subdirs found under {library_dir}")
return violations
for d in base_arch_dirs:
if not _arch_dir_name_is_base(d.name):
violations.append(
f"library subdir name carries target features (must be bare base arch): {d}"
)
for arch_dir in base_arch_dirs:
base = arch_dir.name
if not _arch_dir_name_is_base(base):
continue
entries: Set[str] = {p.name for p in arch_dir.iterdir() if p.is_file()}
for fname in sorted(entries):
if fname.endswith(".dat") and (fname + ".zlib") in entries:
violations.append(
f"both compressed and uncompressed payloads present in {arch_dir}: "
f"{fname} and {fname}.zlib (stale .dat shadows the .zlib at runtime; "
f"the producer must remove the uncompressed sibling)"
)
for template in REQUIRED_PER_BASE_FILES:
if not _has_required_file(entries, template, base):
violations.append(f"missing required file in {arch_dir}: {template.format(arch=base)}")
master_present = any(
t.format(arch=base) in entries for t in TENSILE_MASTER_CANDIDATES
)
lazy_present = any(
t.format(arch=base) in entries for t in TENSILE_LAZY_CANDIDATES
)
if not (master_present or lazy_present):
violations.append(
f"missing TensileLibrary master/lazy file for {base} in {arch_dir} "
f"(expected one of: TensileLibrary_{base}.{{dat,dat.zlib,yaml}} or "
f"TensileLibrary_lazy_{base}.{{dat,dat.zlib,yaml}})"
)
for extra in PER_ARCH_REQUIRED.get(base, ()):
if extra not in entries:
violations.append(
f"missing required {base}-only file in {arch_dir}: {extra}"
)
for fname in entries:
if fname == "metadata.yaml":
continue
if not _filename_arch_matches_dir(fname, base):
violations.append(
f"filename arch suffix does not match dir {base}: {arch_dir / fname}"
)
for arch, extras in PER_ARCH_REQUIRED.items():
for fname in extras:
for d in base_arch_dirs:
if d.name == arch:
continue
stray = d / fname
if stray.is_file():
violations.append(
f"file required only for {arch} found under wrong arch dir: {stray}"
)
return violations
def main(argv: Optional[List[str]] = None) -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument(
"install_root",
type=Path,
help="Install root containing lib/hipblaslt/library/ (or a build tree "
"containing library/).",
)
parser.add_argument(
"--quiet",
"-q",
action="store_true",
help="Suppress success message; exit code still reflects result.",
)
args = parser.parse_args(argv)
violations = validate(args.install_root)
if violations:
print(
f"[validate_library_layout] {len(violations)} layout violation(s) "
f"in {args.install_root}:",
file=sys.stderr,
)
for v in violations:
print(f" - {v}", file=sys.stderr)
return 1
if not args.quiet:
print(f"[validate_library_layout] OK: {args.install_root}")
return 0
if __name__ == "__main__":
sys.exit(main())