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
177 lines
5.8 KiB
Python
177 lines
5.8 KiB
Python
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Branch-local configuration parsing for TheRock."""
|
|
|
|
from dataclasses import dataclass, field
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
from typing import Any
|
|
|
|
from .build_topology import BuildTopology
|
|
|
|
|
|
@dataclass
|
|
class BranchArtifactGroupConfig:
|
|
"""Branch-local configuration for one artifact group."""
|
|
|
|
source_sets: list[str] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class BranchConfig:
|
|
"""Parsed BRANCH_CONFIG.json contents."""
|
|
|
|
flags: dict[str, str] = field(default_factory=dict)
|
|
source_sets: list[str] = field(default_factory=list)
|
|
artifact_groups: dict[str, BranchArtifactGroupConfig] = field(default_factory=dict)
|
|
|
|
|
|
def load_branch_config(
|
|
config_path: Path,
|
|
topology: BuildTopology | None = None,
|
|
*,
|
|
required: bool = False,
|
|
) -> BranchConfig:
|
|
"""Load and validate an optional BRANCH_CONFIG.json file."""
|
|
if not config_path.exists():
|
|
if required:
|
|
raise FileNotFoundError(f"BRANCH_CONFIG.json not found at {config_path}")
|
|
return BranchConfig()
|
|
|
|
with open(config_path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
if not isinstance(data, dict):
|
|
raise ValueError(f"{config_path} must contain a JSON object")
|
|
|
|
flags = _parse_flags(data.get("flags", {}), config_path)
|
|
source_sets = _parse_source_set_list(data.get("source_sets", []), config_path)
|
|
artifact_groups = _parse_artifact_groups(
|
|
data.get("artifact_groups", {}), config_path
|
|
)
|
|
|
|
config = BranchConfig(
|
|
flags=flags,
|
|
source_sets=source_sets,
|
|
artifact_groups=artifact_groups,
|
|
)
|
|
|
|
if topology is not None:
|
|
_validate_config_references(config, topology, config_path)
|
|
|
|
return config
|
|
|
|
|
|
def get_source_sets_for_artifact_groups(
|
|
config: BranchConfig, artifact_group_names: list[str]
|
|
) -> list[str]:
|
|
"""Get branch-configured source sets for a set of artifact groups."""
|
|
result: list[str] = []
|
|
seen: set[str] = set()
|
|
for artifact_group_name in artifact_group_names:
|
|
group_config = config.artifact_groups.get(artifact_group_name)
|
|
if not group_config:
|
|
continue
|
|
for source_set_name in group_config.source_sets:
|
|
if source_set_name not in seen:
|
|
seen.add(source_set_name)
|
|
result.append(source_set_name)
|
|
return result
|
|
|
|
|
|
def _parse_flags(raw_flags: Any, config_path: Path) -> dict[str, str]:
|
|
if raw_flags is None:
|
|
return {}
|
|
if not isinstance(raw_flags, dict):
|
|
raise ValueError(f"{config_path}: 'flags' must be a JSON object")
|
|
|
|
flags: dict[str, str] = {}
|
|
for flag_name, flag_value in raw_flags.items():
|
|
if not isinstance(flag_name, str) or not _is_cmake_identifier(flag_name):
|
|
raise ValueError(f"{config_path}: invalid flag name '{flag_name}'")
|
|
if isinstance(flag_value, bool):
|
|
flags[flag_name] = "ON" if flag_value else "OFF"
|
|
elif isinstance(flag_value, str):
|
|
flags[flag_name] = flag_value
|
|
else:
|
|
raise ValueError(
|
|
f"{config_path}: flag '{flag_name}' value must be a string or boolean"
|
|
)
|
|
return flags
|
|
|
|
|
|
def _parse_source_set_list(raw_source_sets: Any, config_path: Path) -> list[str]:
|
|
if raw_source_sets is None:
|
|
return []
|
|
if not isinstance(raw_source_sets, list) or not all(
|
|
isinstance(name, str) for name in raw_source_sets
|
|
):
|
|
raise ValueError(f"{config_path}: 'source_sets' must be a list of strings")
|
|
return _dedupe(raw_source_sets)
|
|
|
|
|
|
def _parse_artifact_groups(
|
|
raw_artifact_groups: Any, config_path: Path
|
|
) -> dict[str, BranchArtifactGroupConfig]:
|
|
if raw_artifact_groups is None:
|
|
return {}
|
|
if not isinstance(raw_artifact_groups, dict):
|
|
raise ValueError(f"{config_path}: 'artifact_groups' must be a JSON object")
|
|
|
|
artifact_groups: dict[str, BranchArtifactGroupConfig] = {}
|
|
for group_name, group_data in raw_artifact_groups.items():
|
|
if not isinstance(group_name, str):
|
|
raise ValueError(f"{config_path}: artifact group names must be strings")
|
|
if not isinstance(group_data, dict):
|
|
raise ValueError(
|
|
f"{config_path}: artifact_groups.{group_name} must be a JSON object"
|
|
)
|
|
source_sets = _parse_source_set_list(
|
|
group_data.get("source_sets", []), config_path
|
|
)
|
|
artifact_groups[group_name] = BranchArtifactGroupConfig(source_sets=source_sets)
|
|
return artifact_groups
|
|
|
|
|
|
def _validate_config_references(
|
|
config: BranchConfig, topology: BuildTopology, config_path: Path
|
|
) -> None:
|
|
for source_set_name in config.source_sets:
|
|
_validate_source_set_reference(source_set_name, topology, config_path)
|
|
|
|
for group_name, group_config in config.artifact_groups.items():
|
|
if group_name not in topology.artifact_groups:
|
|
raise ValueError(
|
|
f"{config_path}: artifact group '{group_name}' is not defined "
|
|
"in BUILD_TOPOLOGY.toml"
|
|
)
|
|
for source_set_name in group_config.source_sets:
|
|
_validate_source_set_reference(source_set_name, topology, config_path)
|
|
|
|
|
|
def _validate_source_set_reference(
|
|
source_set_name: str, topology: BuildTopology, config_path: Path
|
|
) -> None:
|
|
if source_set_name not in topology.source_sets:
|
|
raise ValueError(
|
|
f"{config_path}: source set '{source_set_name}' is not defined "
|
|
"in BUILD_TOPOLOGY.toml"
|
|
)
|
|
|
|
|
|
def _dedupe(values: list[str]) -> list[str]:
|
|
seen: set[str] = set()
|
|
result: list[str] = []
|
|
for value in values:
|
|
if value in seen:
|
|
continue
|
|
seen.add(value)
|
|
result.append(value)
|
|
return result
|
|
|
|
|
|
def _is_cmake_identifier(value: str) -> bool:
|
|
return re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", value) is not None
|