diff --git a/scripts/pipeline.py b/scripts/pipeline.py index ea8ae5d9..7d590a80 100644 --- a/scripts/pipeline.py +++ b/scripts/pipeline.py @@ -522,10 +522,14 @@ def main() -> None: # upstream declaration no build can repair, so this reports and never gates. # A crash is not a report: without --strict slots.py exits 0 on every # conflict, so a non-zero code means it never finished reading. - ok, _ = run( - [sys.executable, "scripts/slots.py"], - "3b/8 slot arbitration", - ) + # The same platforms and cores verify judged: without these flags the + # arbitration of an --include-archived run never looked at RetroPie. + slots_cmd = [sys.executable, "scripts/slots.py"] + if args.include_archived: + slots_cmd.append("--include-archived") + if args.target: + slots_cmd.extend(["--target", args.target]) + ok, _ = run(slots_cmd, "3b/8 slot arbitration") results["slots"] = ok all_ok &= ok diff --git a/scripts/slots.py b/scripts/slots.py index 0cc8f949..5f7e260f 100644 --- a/scripts/slots.py +++ b/scripts/slots.py @@ -806,13 +806,14 @@ def format_conflict(conflict: Conflict) -> str: def scan_platform( - platform: str, profiles: dict, db: dict, platforms_dir: str = "platforms" + platform: str, profiles: dict, db: dict, platforms_dir: str = "platforms", + target_cores: set[str] | None = None, ) -> list[Conflict]: """Conflicts on one platform, using the cores that platform actually runs.""" from common import load_platform_config, resolve_platform_cores config = load_platform_config(platform, platforms_dir) - keys = resolve_platform_cores(config, profiles) + keys = resolve_platform_cores(config, profiles, target_cores=target_cores) relevant = {k: profiles[k] for k in keys if k in profiles} # The same evidence the builder and the verifier resolve with: without # the ZIP index and the data-directory registry the arbitration judged @@ -833,6 +834,7 @@ def main() -> int: import json from common import ( + build_target_cores_cache, list_registered_platforms, load_database, load_emulator_profiles, @@ -850,6 +852,13 @@ def main() -> int: parser.add_argument("--platforms-dir", default="platforms") parser.add_argument("--emulators-dir", default="emulators") parser.add_argument("--json", action="store_true", help="JSON output") + parser.add_argument( + "--include-archived", action="store_true", + help="with all platforms, the archived ones too (RetroPie)", + ) + parser.add_argument( + "--target", help="judge only the cores a hardware target builds", + ) parser.add_argument( "--strict", action="store_true", @@ -862,16 +871,30 @@ def main() -> int: profiles = load_emulator_profiles(args.emulators_dir) if args.platform: require_platform(args.platform, args.platforms_dir) + if args.include_archived and args.platform: + parser.error("--include-archived widens the list of all platforms; --platform names one") names = ( [args.platform] if args.platform - else list_registered_platforms(args.platforms_dir) + else list_registered_platforms( + args.platforms_dir, include_archived=args.include_archived + ) ) + target_cores: dict[str, set[str] | None] = {} + if args.target: + try: + target_cores, names = build_target_cores_cache( + names, args.target, args.platforms_dir, is_all=not args.platform + ) + except (FileNotFoundError, ValueError) as exc: + parser.error(str(exc)) found: dict[str, list[Conflict]] = {} collided: dict[str, list[Collision]] = {} for name in names: - conflicts = scan_platform(name, profiles, db, args.platforms_dir) + conflicts = scan_platform( + name, profiles, db, args.platforms_dir, target_cores.get(name) + ) if conflicts: found[name] = conflicts collisions = find_collisions( diff --git a/tests/test_slots.py b/tests/test_slots.py index d3afb088..e6dcbfb3 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -809,5 +809,30 @@ class AnArchiveIsClaimedByItsMembers(unittest.TestCase): self.assertEqual(slots.find_conflicts(config, self.profiles, self.db, "system"), []) + +class SlotsJudgeWhatVerifyJudged(unittest.TestCase): + """pipeline --include-archived verified RetroPie and built its pack, but + step 3b ran slots.py without the flag (it had none) and never arbitrated + RetroPie's 31 contested destinations; --target was dropped the same way.""" + + def test_the_pipeline_passes_both_flags(self): + source = (Path(__file__).resolve().parents[1] / "scripts" / "pipeline.py").read_text() + step = source[source.index('slots_cmd = [sys.executable, "scripts/slots.py"]'):] + step = step[:step.index("3b/8")] + self.assertIn('slots_cmd.append("--include-archived")', step) + self.assertIn('slots_cmd.extend(["--target", args.target])', step) + + def test_slots_accepts_them(self): + import subprocess + + root = Path(__file__).resolve().parents[1] + result = subprocess.run( + [sys.executable, "scripts/slots.py", "--include-archived", "--platform", "retropie"], + cwd=root, capture_output=True, text=True, timeout=600, + ) + self.assertEqual(result.returncode, 2, result.stderr) + self.assertIn("--include-archived", result.stderr) + + if __name__ == "__main__": unittest.main()