feat: add load_target_config and list_available_targets to common.py

Loads per-platform target YAML files from platforms/targets/,
resolves aliases and applies add_cores/remove_cores from _overrides.yml.
Includes 7 E2E tests covering alias resolution, overrides, and error paths.
This commit is contained in:
Abdessamad Derraz
2026-03-26 08:41:37 +01:00
parent 8ac64d6143
commit 0549b8945e
2 changed files with 210 additions and 0 deletions

View File

@@ -192,6 +192,100 @@ def list_registered_platforms(
return platforms
def load_target_config(
platform_name: str,
target: str,
platforms_dir: str = "platforms",
) -> set[str]:
"""Load target config and return the set of core names for the given target.
Resolves aliases from _overrides.yml, applies add_cores/remove_cores.
Raises ValueError if target is unknown (with list of available targets).
Raises FileNotFoundError if no target file exists for the platform.
"""
targets_dir = os.path.join(platforms_dir, "targets")
target_file = os.path.join(targets_dir, f"{platform_name}.yml")
if not os.path.exists(target_file):
raise FileNotFoundError(
f"No target config for platform '{platform_name}': {target_file}"
)
with open(target_file) as f:
data = yaml.safe_load(f) or {}
targets = data.get("targets", {})
overrides_file = os.path.join(targets_dir, "_overrides.yml")
overrides = {}
if os.path.exists(overrides_file):
with open(overrides_file) as f:
all_overrides = yaml.safe_load(f) or {}
overrides = all_overrides.get(platform_name, {}).get("targets", {})
alias_index: dict[str, str] = {}
for tname in targets:
alias_index[tname] = tname
for alias in overrides.get(tname, {}).get("aliases", []):
alias_index[alias] = tname
canonical = alias_index.get(target)
if canonical is None:
available = sorted(targets.keys())
aliases = []
for tname, ovr in overrides.items():
for a in ovr.get("aliases", []):
aliases.append(f"{a} -> {tname}")
msg = f"Unknown target '{target}' for platform '{platform_name}'.\n"
msg += f"Available targets: {', '.join(available)}"
if aliases:
msg += f"\nAliases: {', '.join(sorted(aliases))}"
raise ValueError(msg)
cores = set(str(c) for c in targets[canonical].get("cores", []))
ovr = overrides.get(canonical, {})
for c in ovr.get("add_cores", []):
cores.add(str(c))
for c in ovr.get("remove_cores", []):
cores.discard(str(c))
return cores
def list_available_targets(
platform_name: str,
platforms_dir: str = "platforms",
) -> list[dict]:
"""List available targets for a platform with their aliases.
Returns list of dicts with keys: name, architecture, core_count, aliases.
Returns empty list if no target file exists.
"""
targets_dir = os.path.join(platforms_dir, "targets")
target_file = os.path.join(targets_dir, f"{platform_name}.yml")
if not os.path.exists(target_file):
return []
with open(target_file) as f:
data = yaml.safe_load(f) or {}
overrides_file = os.path.join(targets_dir, "_overrides.yml")
overrides = {}
if os.path.exists(overrides_file):
with open(overrides_file) as f:
all_overrides = yaml.safe_load(f) or {}
overrides = all_overrides.get(platform_name, {}).get("targets", {})
result = []
for tname, tdata in sorted(data.get("targets", {}).items()):
aliases = overrides.get(tname, {}).get("aliases", [])
result.append({
"name": tname,
"architecture": tdata.get("architecture", ""),
"core_count": len(tdata.get("cores", [])),
"aliases": aliases,
})
return result
def resolve_local_file(
file_entry: dict,
db: dict,

View File

@@ -1175,6 +1175,122 @@ class TestE2E(unittest.TestCase):
resolved = resolve_platform_cores(config, profiles)
self.assertIn("test_alias_core", resolved)
# ---------------------------------------------------------------
# Target config tests (Task 1)
# ---------------------------------------------------------------
def _write_target_fixtures(self):
"""Create target config fixtures for testing."""
targets_dir = os.path.join(self.platforms_dir, "targets")
os.makedirs(targets_dir, exist_ok=True)
target_config = {
"platform": "testplatform",
"source": "test",
"scraped_at": "2026-01-01T00:00:00Z",
"targets": {
"target-full": {
"architecture": "x86_64",
"cores": ["core_a", "core_b", "core_c"],
},
"target-minimal": {
"architecture": "armv7",
"cores": ["core_a"],
},
},
}
with open(os.path.join(targets_dir, "testplatform.yml"), "w") as f:
yaml.dump(target_config, f)
single_config = {
"platform": "singleplatform",
"source": "test",
"scraped_at": "2026-01-01T00:00:00Z",
"targets": {
"only-target": {
"architecture": "x86_64",
"cores": ["core_a", "core_b"],
},
},
}
with open(os.path.join(targets_dir, "singleplatform.yml"), "w") as f:
yaml.dump(single_config, f)
overrides = {
"testplatform": {
"targets": {
"target-full": {
"aliases": ["full", "pc", "desktop"],
"add_cores": ["core_d"],
"remove_cores": ["core_c"],
},
"target-minimal": {
"aliases": ["minimal", "arm"],
},
},
},
}
with open(os.path.join(targets_dir, "_overrides.yml"), "w") as f:
yaml.dump(overrides, f)
def test_load_target_config(self):
self._write_target_fixtures()
from common import load_target_config
cores = load_target_config("testplatform", "target-minimal", self.platforms_dir)
self.assertEqual(cores, {"core_a"})
def test_target_alias_resolution(self):
self._write_target_fixtures()
from common import load_target_config
cores = load_target_config("testplatform", "full", self.platforms_dir)
self.assertEqual(cores, {"core_a", "core_b", "core_d"})
def test_target_unknown_error(self):
self._write_target_fixtures()
from common import load_target_config
with self.assertRaises(ValueError) as ctx:
load_target_config("testplatform", "nonexistent", self.platforms_dir)
self.assertIn("target-full", str(ctx.exception))
self.assertIn("target-minimal", str(ctx.exception))
def test_target_override_add_remove(self):
self._write_target_fixtures()
from common import load_target_config
cores = load_target_config("testplatform", "full", self.platforms_dir)
self.assertIn("core_d", cores)
self.assertNotIn("core_c", cores)
self.assertIn("core_a", cores)
self.assertIn("core_b", cores)
def test_target_single_target_noop(self):
self._write_target_fixtures()
from common import load_target_config
cores = load_target_config("singleplatform", "only-target", self.platforms_dir)
self.assertEqual(cores, {"core_a", "core_b"})
def test_target_inherits(self):
self._write_target_fixtures()
targets_dir = os.path.join(self.platforms_dir, "targets")
child_config = {
"platform": "childplatform",
"source": "test",
"scraped_at": "2026-01-01T00:00:00Z",
"targets": {
"target-full": {
"architecture": "x86_64",
"cores": ["core_a"],
},
},
}
with open(os.path.join(targets_dir, "childplatform.yml"), "w") as f:
yaml.dump(child_config, f)
from common import load_target_config
parent = load_target_config("testplatform", "target-minimal", self.platforms_dir)
child = load_target_config("childplatform", "target-full", self.platforms_dir)
self.assertEqual(parent, {"core_a"})
self.assertEqual(child, {"core_a"})
self.assertNotEqual(
load_target_config("testplatform", "full", self.platforms_dir),
child,
)
if __name__ == "__main__":
unittest.main()