Skip to content
Draft
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c4561de
ci(consumer-graph): make static parser the authoritative graph generator
endurthiabhilash Sep 30, 2026
2e8d190
ci(consumer-graph): commit generated subtree_map as a drift-checked file
endurthiabhilash Sep 30, 2026
cadab7d
ci(test-selection): resolve subtrees from the generated subtree_map
endurthiabhilash Sep 30, 2026
1e0a041
ci(consumer-graph): generate report-only source-dir map
endurthiabhilash Oct 1, 2026
227aba5
ci(test-labels): guard test-label consistency and label-group duplica…
endurthiabhilash Oct 1, 2026
2ff1c9d
ci(consumer-graph): remove the configure-time consumer-graph emit
endurthiabhilash Oct 1, 2026
f6cf619
ci(consumer-graph): degrade gracefully on unresolvable provide_artifact
endurthiabhilash Oct 1, 2026
5878ad8
ci(test-selection): harden subtree-alias map loading
endurthiabhilash Oct 1, 2026
9342855
ci(consumer-graph): regenerate derived maps against latest main
endurthiabhilash Oct 1, 2026
b4e539c
refactor(consumer-graph): trim narrating comments; drop dead edge rec…
endurthiabhilash Oct 1, 2026
0535246
refactor(consumer-graph): plainer test names and comment wording
endurthiabhilash Oct 1, 2026
b0da1b5
test(consumer-graph): add generator, external-repo, and robustness co…
endurthiabhilash Oct 2, 2026
93a8849
refactor(consumer-graph): plainer comments; guard --check against mis…
endurthiabhilash Oct 5, 2026
7025aee
ci(consumer-graph): rename test files to fix collision and naming hook
endurthiabhilash Oct 5, 2026
13ca00c
ci(consumer-graph): regenerate derived maps to match main
endurthiabhilash Oct 7, 2026
5df019b
fix(ci): make every test-matrix job reachable by scoped selection
tony-davis Oct 13, 2026
27de761
ci(consumer-graph): make static parser the authoritative graph generator
endurthiabhilash Sep 30, 2026
52b5e5d
ci(consumer-graph): commit generated subtree_map as a drift-checked file
endurthiabhilash Sep 30, 2026
23b64a2
ci(test-selection): resolve subtrees from the generated subtree_map
endurthiabhilash Sep 30, 2026
f0e4cdd
ci(consumer-graph): generate report-only source-dir map
endurthiabhilash Oct 1, 2026
7991040
ci(test-labels): guard test-label consistency and label-group duplica…
endurthiabhilash Oct 1, 2026
29a671f
ci(consumer-graph): remove the configure-time consumer-graph emit
endurthiabhilash Oct 1, 2026
2eb4260
ci(consumer-graph): degrade gracefully on unresolvable provide_artifact
endurthiabhilash Oct 1, 2026
57e1434
ci(test-selection): harden subtree-alias map loading
endurthiabhilash Oct 1, 2026
01387c2
ci(consumer-graph): regenerate derived maps against latest main
endurthiabhilash Oct 1, 2026
82f67f9
refactor(consumer-graph): trim narrating comments; drop dead edge rec…
endurthiabhilash Oct 1, 2026
d99237f
refactor(consumer-graph): plainer test names and comment wording
endurthiabhilash Oct 1, 2026
f82e2fc
test(consumer-graph): add generator, external-repo, and robustness co…
endurthiabhilash Oct 2, 2026
5f41a25
refactor(consumer-graph): plainer comments; guard --check against mis…
endurthiabhilash Oct 5, 2026
1de981e
ci(consumer-graph): rename test files to fix collision and naming hook
endurthiabhilash Oct 5, 2026
30047e4
ci(consumer-graph): regenerate derived maps to match main
endurthiabhilash Oct 7, 2026
bf34239
ci(consumer-graph): regenerate maps after rebase for profiler-hub
endurthiabhilash Oct 13, 2026
cfd0826
Merge users/tony-davis/selector-reachability (test-job reachability)
endurthiabhilash Oct 13, 2026
d5a2d37
refactor(consumer-graph): resolve subtree map from --therock-dir
endurthiabhilash Oct 14, 2026
7ba79a4
refactor(consumer-graph): assert source-dir-map invariants; selector …
endurthiabhilash Oct 14, 2026
43918f1
test(consumer-graph): pass subtree_alias_map to _normalize_changed_pr…
endurthiabhilash Oct 14, 2026
a51a433
Merge branch 'main' into users/aendurth/issue-7782-pr2-activation
endurthiabhilash Oct 19, 2026
f22bcbc
test(consumer-graph): rename override-retirement test and clarify mes…
endurthiabhilash Nov 6, 2026
d496778
Merge origin/main into users/aendurth/issue-7782-pr2-activation
endurthiabhilash Nov 7, 2026
d701f11
refactor(consumer-graph): drop the source-dir map until it has a cons…
endurthiabhilash Nov 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ jobs:
- name: "Determine test dependencies"
id: test_deps
# Reads the committed consumer graph directly (no fetch/configure); kept
# in sync by test_consumer_graph_drift.yml.
# in sync by the consumer-graph parser test in the Test build_tools job.
shell: bash
env:
CHANGED_PROJECTS: ${{ inputs.changed_projects }}
Expand Down
61 changes: 0 additions & 61 deletions .github/workflows/test_consumer_graph_drift.yml

This file was deleted.

3 changes: 0 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,3 @@ therock_subproject_merge_compile_commands()

# Write artifact→subprojects manifest for Python scripts to use
therock_write_subproject_manifest()

include(therock_emit_consumer_graph)
therock_emit_consumer_graph("${CMAKE_BINARY_DIR}/therock_consumer_graph.json")
116 changes: 113 additions & 3 deletions build_tools/_therock_utils/cmake_consumer_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@
_DECLARATION_FLAGS | _DECLARATION_ONE_VALUE_ARGS | _DECLARATION_MULTI_VALUE_ARGS
)

# Keyword vocabulary of therock_provide_artifact(); mirrors the
# cmake_parse_arguments signature in cmake/therock_artifacts.cmake. Used to
# split out the SUBPROJECT_DEPS list for the source-dir map.
_PROVIDE_ARTIFACT_KEYWORDS = frozenset(
{"TARGET_NEUTRAL", "DESCRIPTOR", "DISTRIBUTION", "COMPONENTS", "SUBPROJECT_DEPS"}
)

# TODO: mirrors CMake's therock_compiler_toolchain_subproject() (the CMake-side
# source); share one definition via a data file if a third toolchain is added.
_TOOLCHAIN_SUBPROJECTS = {
Expand Down Expand Up @@ -182,6 +189,9 @@ class AnalysisResult:
subprojects: dict[str, Subproject]
skipped_paths: list[SkippedPath]
repository_root: Path
# artifact slice name -> set of SUBPROJECT_DEPS subproject names, from
# therock_provide_artifact(). Empty when no provide_artifact calls were reached.
artifacts: dict[str, set[str]] = field(default_factory=dict)

@property
def declaration_count(self) -> int:
Expand Down Expand Up @@ -243,6 +253,49 @@ def build_subtree_map(self) -> dict[str, list[str]]:
subtree: sorted(keys) for subtree, keys in sorted(subtree_to_keys.items())
}

def build_source_dir_map(self) -> dict[str, list[str]]:
"""Map each artifact to the source subtree(s) its SUBPROJECT_DEPS build from.

For every ``therock_provide_artifact()`` slice, join each ``SUBPROJECT_DEPS``
subproject to its ``EXTERNAL_SOURCE_DIR`` subtree (relativized against the
rocm-libraries / rocm-systems roots, as in ``build_subtree_map``). This map is
informational; it does not replace ``BUILD_TOPOLOGY.toml`` ``source_paths``,
which are hand-curated and only partly overlap. Artifacts whose deps resolve to
no statically-captured source dir are omitted.
"""
roots = (
self.repository_root / "rocm-libraries",
self.repository_root / "rocm-systems",
)

def subtrees_for(subproject_key: str) -> set[str]:
subproject = self.subprojects.get(subproject_key)
if subproject is None:
return set()
result: set[str] = set()
for source_dir in subproject.external_source_dirs:
source_path = Path(source_dir)
for root in roots:
try:
relative = source_path.relative_to(root)
except ValueError:
continue
result.add(relative.as_posix())
break
return result

artifact_to_subtrees: dict[str, set[str]] = {}
for artifact, deps in self.artifacts.items():
subtrees: set[str] = set()
for dep in deps:
subtrees |= subtrees_for(dep.lower())
if subtrees:
artifact_to_subtrees[artifact] = subtrees
return {
artifact: sorted(subtrees)
for artifact, subtrees in sorted(artifact_to_subtrees.items())
}

def dangling_dependencies(self) -> dict[str, list[str]]:
"""Return dependencies that do not name any discovered subproject."""
known = {name.lower() for name in self.subprojects}
Expand Down Expand Up @@ -437,6 +490,7 @@ def __init__(
self._tracked_declaration_files: set[Path] = set()
self._declaration_files: set[Path] = set()
self._subprojects: dict[str, Subproject] = {}
self._artifacts: dict[str, set[str]] = {}
self._skipped_paths: list[SkippedPath] = []
self._active_files: set[Path] = set()

Expand All @@ -461,6 +515,7 @@ def analyze(self) -> AnalysisResult:
subprojects=copy.deepcopy(self._subprojects),
skipped_paths=list(self._skipped_paths),
repository_root=self.repository_root,
artifacts=copy.deepcopy(self._artifacts),
)

def _parse_tracked_inventory(self) -> None:
Expand Down Expand Up @@ -728,6 +783,8 @@ def _execute_command(
self._execute_add_subdirectory(node, environment, relative_path)
elif identifier == "therock_cmake_subproject_declare":
self._record_declaration(node, environment, relative_path)
elif identifier == "therock_provide_artifact":
self._record_artifact(node, environment, relative_path)

def _execute_list(self, node: cmake_ast.Command, environment: Environment) -> None:
"""Model ``list(APPEND|PREPEND|REMOVE_ITEM VAR ...)``."""
Expand Down Expand Up @@ -904,6 +961,56 @@ def _record_declaration(
subproject.locations.add(SourceLocation(path=relative_path, line=node.line))
self._declaration_files.add(relative_path)

def _record_artifact(
self,
node: cmake_ast.Command,
environment: Environment,
relative_path: Path,
) -> None:
"""Extract one ``therock_provide_artifact()`` slice's SUBPROJECT_DEPS.

Feeds the informational ``source_dir_map`` only, not the consumer graph.
Resolution failures are non-fatal: the artifact is omitted with a SkippedPath
diagnostic instead of aborting the analysis.
"""
location = SourceLocation(path=relative_path, line=node.line)
if not node.args:
self._skipped_paths.append(
SkippedPath(
location=location,
expression="therock_provide_artifact",
reason="provide_artifact has no name",
)
)
return
name_expansion = _expand_token(node.args[0], environment)
if name_expansion.unresolved or len(name_expansion.values) != 1:
self._skipped_paths.append(
SkippedPath(
location=location,
expression=node.args[0].value,
reason="cannot resolve exactly one artifact name",
)
)
return
name = next(iter(name_expansion.values))
sections = _declaration_sections(node.args[1:], _PROVIDE_ARTIFACT_KEYWORDS)
expansion = _expand_tokens(sections.get("SUBPROJECT_DEPS", []), environment)
if expansion.unresolved:
self._skipped_paths.append(
SkippedPath(
location=location,
expression=name,
reason=(
"unresolved SUBPROJECT_DEPS variables: "
f"{', '.join(sorted(expansion.unresolved))}"
),
)
)
return
deps = {value for value in expansion.values if value}
self._artifacts.setdefault(name, set()).update(deps)

def _resolve_dependency_section(
self,
tokens: list[Token],
Expand Down Expand Up @@ -934,14 +1041,17 @@ def _resolve_source_dir_section(
return {value for value in expansion.values if value}


def _declaration_sections(tokens: list[Token]) -> dict[str, list[Token]]:
"""Group a declaration's argument tokens by their keyword (BUILD_DEPS, ...)."""
def _declaration_sections(
tokens: list[Token],
keywords: frozenset[str] | set[str] = _DECLARATION_KEYWORDS,
) -> dict[str, list[Token]]:
"""Group an argument list's tokens by their keyword (BUILD_DEPS, ...)."""
sections: dict[str, list[Token]] = {}
current_keyword: str | None = None
for token in tokens:
if token.kind == "COMMENT":
continue
if token.value in _DECLARATION_KEYWORDS:
if token.value in keywords:
current_keyword = token.value
sections.setdefault(current_keyword, [])
elif current_keyword is not None:
Expand Down
Loading
Loading