Files
TheRock/test_tools/determine_rocm_test_dependencies.py
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

193 lines
6.0 KiB
Python
Executable File

# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""
Compute subproject test dependencies by parsing CMakeLists.txt files.
Example:
$ python test_tools/determine_rocm_test_dependencies.py --changed-projects rocSPARSE
["hipsparse", "rocsparse"]
"""
import argparse
import json
import re
import sys
from pathlib import Path
sys.path.insert(
0, str(Path(__file__).resolve().parents[1] / "build_tools" / "github_actions")
)
from github_actions_api import gha_set_output
def parse_cmake_test_subprojects(therock_dir):
"""Parse CMakeLists.txt files to extract TEST_SUBPROJECTS declarations.
Returns dict mapping subproject name (lowercase) -> list of test dependencies (lowercase).
"""
test_deps = {}
cmake_files = list(Path(therock_dir).rglob("CMakeLists.txt"))
for cmake_file in cmake_files:
content = cmake_file.read_text()
# Find all therock_cmake_subproject_declare blocks
pattern = r"therock_cmake_subproject_declare\s*\(\s*([\w-]+)(.*?)\)"
for match in re.finditer(pattern, content, re.DOTALL):
subproject_name = match.group(1).lower()
block_content = match.group(2)
# Look for TEST_SUBPROJECTS within this block
# Match TEST_SUBPROJECTS with optional list of dependencies
# Empty TEST_SUBPROJECTS is valid (only tests itself)
test_subprojects_match = re.search(
r"TEST_SUBPROJECTS(?:\s+((?:[\w-]+\s*)+))?", block_content
)
if test_subprojects_match:
deps_str = (test_subprojects_match.group(1) or "").strip()
deps = [d.strip().lower() for d in deps_str.split() if d.strip()]
test_deps[subproject_name] = deps
return test_deps
def get_subprojects_to_test(changed_subprojects, therock_dir=None):
"""Get all subprojects to test when given subprojects change."""
if therock_dir is None:
therock_dir = Path.cwd()
else:
therock_dir = Path(therock_dir)
test_deps = parse_cmake_test_subprojects(therock_dir)
# Convert inputs to lowercase
changed_lower = [p.lower() for p in changed_subprojects]
result = set(changed_lower)
for changed in changed_lower:
if changed in test_deps:
result.update(test_deps[changed])
return result
def get_rocm_test_dependencies(changed_subprojects, therock_dir=None):
"""Get all subprojects to test when specific subprojects change."""
return get_subprojects_to_test(changed_subprojects, therock_dir)
def list_subprojects(therock_dir=None, show_deps=False):
"""List all subprojects with TEST_SUBPROJECTS.
Args:
therock_dir: Path to TheRock directory
show_deps: If True, return dict with deps; if False, return list of names
"""
if therock_dir is None:
therock_dir = Path.cwd()
test_deps = parse_cmake_test_subprojects(therock_dir)
if show_deps:
# Return dict with "empty" indicator for subprojects with no test deps
result = {}
for name in sorted(test_deps.keys()):
deps = test_deps[name]
result[name] = deps if deps else "empty"
return result
return sorted(test_deps.keys())
def main():
parser = argparse.ArgumentParser(
description="Compute subproject test dependencies by parsing CMakeLists.txt"
)
parser.add_argument(
"--therock-dir", type=str, default=".", help="TheRock directory"
)
parser.add_argument(
"--changed-projects",
type=str,
nargs="*",
metavar="PROJECT",
help="Project(s) to test. Accepts space-separated or comma-separated list. "
"Supports 'rocblas' or 'projects/rocblas' format.",
)
parser.add_argument(
"--list-subprojects", action="store_true", help="List all subprojects"
)
parser.add_argument(
"--show-deps",
action="store_true",
help="With --list-subprojects, show dependencies (or 'empty' if none)",
)
parser.add_argument(
"--format",
choices=["json", "list"],
default="json",
help="Output format: json (default) or list (newline-separated)",
)
parser.add_argument(
"--gha-output",
action="store_true",
help="Write projects_to_test to GITHUB_OUTPUT",
)
args = parser.parse_args()
therock_dir = Path(args.therock_dir).resolve()
if args.list_subprojects:
result = list_subprojects(therock_dir, show_deps=args.show_deps)
print(json.dumps(result, indent=2))
return
# Parse changed_projects: handle comma-separated and space-separated input
changed = args.changed_projects
if changed:
# Flatten comma-separated values (e.g., "rocblas,hipblas" -> ["rocblas", "hipblas"])
flattened = []
for item in changed:
flattened.extend(p.strip() for p in item.split(",") if p.strip())
changed = flattened
# Normalize path format: "projects/rocblas" -> "rocblas"
if changed:
changed = [p.removeprefix("projects/") for p in changed]
# Warn about unrecognized projects to catch typos (false-green risk)
known = set(parse_cmake_test_subprojects(therock_dir).keys())
unknown = [p for p in changed if p.lower() not in known]
if unknown:
print(
f"Warning: unrecognized project(s) {unknown}; "
"no tests will be selected for them",
file=sys.stderr,
)
# If no projects specified, output "*" for all tests
if not changed:
if args.gha_output:
gha_set_output({"projects_to_test": "*"})
else:
print("*")
return
result = get_subprojects_to_test(changed, therock_dir)
projects_to_test = ",".join(sorted(result))
if args.gha_output:
gha_set_output({"projects_to_test": projects_to_test})
elif args.format == "json":
print(json.dumps(sorted(result)))
else:
for item in sorted(result):
print(item)
if __name__ == "__main__":
main()