Repository navigation
feat(ci): derive test-subproject selection from the consumer graph #6941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ScottTodd
merged 13 commits into
ROCm:main
from
endurthiabhilash:users/aendurth/support-dynamic-subproject-testing
Aug 7, 2026
Merged
Changes from 11 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
dd17fa4
feat: derive test subprojects from consumer graph and BUILD_TOPOLOGY …
endurthiabhilash dfbc289
feat: restore TEST_OVERRIDE_CHANGED_PROJECTS env override
endurthiabhilash 92160ae
docs: add RFC0013 consumer-graph test selection
endurthiabhilash 8f468a4
docs: generalize RFC0013 to DEFCON 1-5 fan-out ladder
endurthiabhilash 2dd805f
Merge branch 'main' into users/aendurth/support-dynamic-subproject-te…
endurthiabhilash a3f3e4e
feat(test): add consumer-graph drift check and committed graph
endurthiabhilash 97044aa
feat(test): expand test_policies.toml to cover downstream dependents
endurthiabhilash 727e442
chore: remove TEST_OVERRIDE_CHANGED_PROJECTS env override
endurthiabhilash a1d3653
refactor(test): address review on consumer-graph test selection
endurthiabhilash e61b89d
chore: regenerate consumer graph with therock-libiberty edge
endurthiabhilash 4c07efe
docs: add TODO for levels 1 and 2 test-tier follow-up
endurthiabhilash 5c00ba4
refactor(test): drop reverse-direction backend test_include couplings
endurthiabhilash 4617f1a
Merge branch 'main' into users/aendurth/support-dynamic-subproject-te…
endurthiabhilash File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # Copyright Advanced Micro Devices, Inc. | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| # Regenerates the consumer graph from a full-tree configure and fails if it | ||
| # differs from the committed test_tools/therock_consumer_graph.json. Runs only | ||
| # when graph-affecting files change, so the configure cost stays off the per-PR | ||
| # path (see RFC0013 "CI integration"). | ||
|
|
||
| name: Consumer Graph Drift Check | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - "**/CMakeLists.txt" | ||
| - "cmake/therock_emit_consumer_graph.cmake" | ||
| - "cmake/therock_subproject.cmake" | ||
| - "test_tools/therock_consumer_graph.json" | ||
| - ".github/workflows/test_consumer_graph_drift.yml" | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| consumer_graph_drift: | ||
| name: "Consumer graph drift check" | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - name: Checking out repository | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
|
|
||
| - name: Setting up Python | ||
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | ||
| with: | ||
| python-version: 3.12 | ||
|
|
||
| - name: Install Python deps | ||
| run: pip install -r requirements.txt | ||
|
|
||
| - name: Fetch sources | ||
| # All subproject sources must be present for the graph to be complete. | ||
| run: python3 ./build_tools/fetch_sources.py --jobs 12 --depth 1 | ||
|
|
||
| - name: Configure (emits the consumer graph) | ||
| # Configure only, no build. gfx94X-dcgpu enables every subproject; some | ||
| # (hipSPARSELt, rocprofiler-compute) are excluded on gfx110X, which would | ||
| # drop their edges. | ||
| run: | | ||
| cmake -B build -S . -GNinja \ | ||
| -DTHEROCK_ENABLE_ALL=ON \ | ||
| -DTHEROCK_AMDGPU_FAMILIES=gfx94X-dcgpu | ||
|
|
||
| - name: Check committed consumer graph is up to date | ||
| run: python3 ./build_tools/github_actions/check_consumer_graph_drift.py --check |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
build_tools/github_actions/check_consumer_graph_drift.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| #!/usr/bin/env python3 | ||
| # Copyright Advanced Micro Devices, Inc. | ||
| # SPDX-License-Identifier: MIT | ||
| """Drift check for the committed consumer graph. | ||
|
|
||
| Keeps test_tools/therock_consumer_graph.json in sync with a freshly emitted graph | ||
| (written to build/ by a configure). `--check` compares the two, failing on any | ||
| edge change; `--write` overwrites the committed copy. Both normalize (sorted keys | ||
| and consumer lists, 2-space indent, trailing newline) so only genuine | ||
| dependency-edge changes register, not the emitter's registration order. | ||
| """ | ||
|
|
||
| import argparse | ||
| import difflib | ||
| import json | ||
| import os | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| _REPO_ROOT = Path(__file__).resolve().parents[2] | ||
| _GRAPH_FILENAME = "therock_consumer_graph.json" | ||
| _COMMITTED_PATH = _REPO_ROOT / "test_tools" / _GRAPH_FILENAME | ||
| # Which build tree holds the freshly emitted graph; overridable via --build-dir. | ||
| _DEFAULT_BUILD_DIR = Path(os.environ.get("THEROCK_BUILD_DIR", _REPO_ROOT / "build")) | ||
|
|
||
|
|
||
| def normalize(path: Path) -> str: | ||
| """Return the graph as canonical JSON: sorted keys and consumer lists, | ||
| 2-space indent, trailing newline. Used by both --check and --write.""" | ||
| graph = json.loads(path.read_text()) | ||
| norm = { | ||
| key: {"consumers": sorted(graph[key].get("consumers", []))} | ||
| for key in sorted(graph) | ||
| } | ||
| return json.dumps(norm, indent=2, sort_keys=True) + "\n" | ||
|
|
||
|
|
||
| def check(committed_path: Path, emitted_path: Path) -> int: | ||
| """Fail (return 1) if the committed graph differs from the emitted one.""" | ||
| committed = normalize(committed_path) | ||
| emitted = normalize(emitted_path) | ||
|
|
||
| if committed == emitted: | ||
| print("Consumer graph is up to date.") | ||
| return 0 | ||
|
|
||
| print("::error::test_tools/therock_consumer_graph.json is out of date.") | ||
| print( | ||
| "A dependency edge changed but the committed consumer graph was not " | ||
| "regenerated." | ||
| ) | ||
| print("Fix: run a full-tree configure, then regenerate and re-commit:") | ||
| print(" python3 ./build_tools/fetch_sources.py") | ||
| print( | ||
| " cmake -B build -GNinja -DTHEROCK_ENABLE_ALL=ON " | ||
| "-DTHEROCK_AMDGPU_FAMILIES=gfx94X-dcgpu" | ||
| ) | ||
| print( | ||
| " python3 ./build_tools/github_actions/check_consumer_graph_drift.py --write" | ||
| ) | ||
| print(" git add test_tools/therock_consumer_graph.json") | ||
| sys.stdout.writelines( | ||
| difflib.unified_diff( | ||
| committed.splitlines(keepends=True), | ||
| emitted.splitlines(keepends=True), | ||
| fromfile="committed", | ||
| tofile="regenerated", | ||
| ) | ||
| ) | ||
| return 1 | ||
|
|
||
|
|
||
| def write(committed_path: Path, emitted_path: Path) -> int: | ||
| """Overwrite the committed graph with the normalized emitted graph.""" | ||
| committed_path.write_text(normalize(emitted_path)) | ||
| print(f"Wrote normalized consumer graph to {committed_path}.") | ||
| return 0 | ||
|
|
||
|
|
||
| def main(argv: list[str]) -> int: | ||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| mode = parser.add_mutually_exclusive_group(required=True) | ||
| mode.add_argument( | ||
| "--check", | ||
| action="store_true", | ||
| help="Fail if the committed graph differs from build/'s emitted graph.", | ||
| ) | ||
| mode.add_argument( | ||
| "--write", | ||
| action="store_true", | ||
| help="Normalize build/'s emitted graph over the committed copy.", | ||
| ) | ||
| parser.add_argument( | ||
| "--committed", | ||
| type=Path, | ||
| default=_COMMITTED_PATH, | ||
| help="Path to the committed consumer graph (default: test_tools/).", | ||
| ) | ||
| parser.add_argument( | ||
| "--build-dir", | ||
| type=Path, | ||
| default=_DEFAULT_BUILD_DIR, | ||
| help=( | ||
| "Build directory holding the freshly emitted graph " | ||
| "(default: $THEROCK_BUILD_DIR or build/). Ignored if --emitted is set." | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| "--emitted", | ||
| type=Path, | ||
| default=None, | ||
| help="Explicit path to the emitted graph (overrides --build-dir).", | ||
| ) | ||
| args = parser.parse_args(argv) | ||
|
|
||
| emitted = args.emitted or (args.build_dir / _GRAPH_FILENAME) | ||
|
|
||
| if args.write: | ||
| return write(args.committed, emitted) | ||
| return check(args.committed, emitted) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main(sys.argv[1:])) | ||
74 changes: 74 additions & 0 deletions
74
build_tools/github_actions/tests/check_consumer_graph_drift_test.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #!/usr/bin/env python3 | ||
| # Copyright Advanced Micro Devices, Inc. | ||
| # SPDX-License-Identifier: MIT | ||
| """Unit tests for check_consumer_graph_drift.py""" | ||
|
|
||
| import json | ||
| import sys | ||
| import tempfile | ||
| import unittest | ||
| from pathlib import Path | ||
|
|
||
| sys.path.insert(0, str(Path(__file__).parent.parent)) | ||
|
|
||
| from check_consumer_graph_drift import check, normalize, write | ||
|
|
||
|
|
||
| class TestNormalize(unittest.TestCase): | ||
| def _write(self, obj) -> Path: | ||
| path = Path(tempfile.mkdtemp()) / "graph.json" | ||
| path.write_text(json.dumps(obj)) | ||
| return path | ||
|
|
||
| def test_normalize_is_order_insensitive(self) -> None: | ||
| # Same edges, different key/consumer order -> identical normalized text. | ||
| a = self._write({"b": {"consumers": ["z", "a"]}, "a": {"consumers": []}}) | ||
| b = self._write({"a": {"consumers": []}, "b": {"consumers": ["a", "z"]}}) | ||
| self.assertEqual(normalize(a), normalize(b)) | ||
|
|
||
| def test_normalize_has_trailing_newline_and_indent(self) -> None: | ||
| text = normalize(self._write({"a": {"consumers": ["b"]}})) | ||
| self.assertTrue(text.endswith("\n")) | ||
| self.assertIn(' "a"', text) # 2-space indent | ||
|
|
||
| def test_normalize_tolerates_missing_consumers_key(self) -> None: | ||
| # A node with no "consumers" key normalizes to an empty list, not a crash. | ||
| text = normalize(self._write({"a": {}})) | ||
| self.assertIn('"consumers": []', text) | ||
|
|
||
|
|
||
| class TestCheck(unittest.TestCase): | ||
| def _write(self, obj) -> Path: | ||
| path = Path(tempfile.mkdtemp()) / "graph.json" | ||
| path.write_text(json.dumps(obj)) | ||
| return path | ||
|
|
||
| def test_check_passes_when_equal_modulo_order(self) -> None: | ||
| committed = self._write({"a": {"consumers": ["b", "c"]}}) | ||
| emitted = self._write({"a": {"consumers": ["c", "b"]}}) | ||
| self.assertEqual(check(committed, emitted), 0) | ||
|
|
||
| def test_check_fails_on_edge_change(self) -> None: | ||
| committed = self._write({"a": {"consumers": ["b"]}}) | ||
| emitted = self._write({"a": {"consumers": ["b", "c"]}}) | ||
| self.assertEqual(check(committed, emitted), 1) | ||
|
|
||
|
|
||
| class TestWrite(unittest.TestCase): | ||
| def test_write_makes_check_pass(self) -> None: | ||
| root = Path(tempfile.mkdtemp()) | ||
| committed = root / "committed.json" | ||
| emitted = root / "emitted.json" | ||
| # Stale committed copy, differently-ordered emitted graph. | ||
| committed.write_text(json.dumps({"a": {"consumers": ["stale"]}})) | ||
| emitted.write_text(json.dumps({"a": {"consumers": ["c", "b"]}})) | ||
|
|
||
| self.assertEqual(write(committed, emitted), 0) | ||
| # After write, committed matches emitted and check passes. | ||
| self.assertEqual(check(committed, emitted), 0) | ||
| # And it was written in normalized form (sorted, trailing newline). | ||
| self.assertEqual(committed.read_text(), normalize(emitted)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # Copyright Advanced Micro Devices, Inc. | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| # therock_emit_consumer_graph(output_file) | ||
| # | ||
| # Serializes the consumer graph (from the THEROCK_ALL_SUBPROJECTS / | ||
| # THEROCK_DIRECT_CONSUMERS_OF_* global properties) to JSON at <output_file>. Call | ||
| # at the end of the top-level CMakeLists.txt, after all subprojects are declared. | ||
| # | ||
| # Schema (reverse-dependency edges only): | ||
| # { "<subproject>": { "consumers": ["<consumer>", ...] }, ... } | ||
| # | ||
| # The emit writes build/therock_consumer_graph.json, which refreshes the committed | ||
| # copy at test_tools/therock_consumer_graph.json. The committed copy is read | ||
| # directly by CI (no configure on the hot path) and kept honest by the drift check | ||
| # in .github/workflows/test_consumer_graph_drift.yml. It is generated-only; never | ||
| # hand-edit it. | ||
| function(therock_emit_consumer_graph output_file) | ||
| get_property(_all GLOBAL PROPERTY THEROCK_ALL_SUBPROJECTS) | ||
|
|
||
| set(_json "{\n") | ||
| set(_first_proj TRUE) | ||
|
|
||
| foreach(_proj IN LISTS _all) | ||
| get_property(_consumers GLOBAL PROPERTY "THEROCK_DIRECT_CONSUMERS_OF_${_proj}") | ||
| if(_consumers) | ||
| list(REMOVE_DUPLICATES _consumers) | ||
| endif() | ||
|
|
||
| if(NOT _first_proj) | ||
| string(APPEND _json ",\n") | ||
| endif() | ||
| set(_first_proj FALSE) | ||
|
|
||
| string(TOLOWER "${_proj}" _key) | ||
|
|
||
| # Build JSON array of consumers. | ||
| set(_arr "") | ||
| set(_first_c TRUE) | ||
| foreach(_c IN LISTS _consumers) | ||
| string(TOLOWER "${_c}" _cv) | ||
| if(NOT _first_c) | ||
| string(APPEND _arr ", ") | ||
| endif() | ||
| set(_first_c FALSE) | ||
| string(APPEND _arr "\"${_cv}\"") | ||
| endforeach() | ||
|
|
||
| string(APPEND _json " \"${_key}\": {\n") | ||
| string(APPEND _json " \"consumers\": [${_arr}]\n") | ||
| string(APPEND _json " }") | ||
| endforeach() | ||
|
|
||
| string(APPEND _json "\n}\n") | ||
| file(WRITE "${output_file}" "${_json}") | ||
| message(STATUS "Wrote consumer graph to ${output_file}") | ||
| endfunction() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also update the recommended family to gfx94X-dcgpu here if addressing my related comment