From fb40b903ca554b20c977037e2ad77c03a6e25553 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 10:08:17 +0200 Subject: [PATCH] fix: refuse an unknown --system before the refresh The platform's systems are known once its config is loaded. The filter ran inside the build, after the output lock and a refresh of every data directory, and ended on a WARNING with exit 1. It is now a usage error before any work. --- scripts/generate_pack.py | 36 +++++++++++++++++++++++++++++++++ tests/test_audit_regressions.py | 28 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/scripts/generate_pack.py b/scripts/generate_pack.py index dbd70406..da01c68d 100644 --- a/scripts/generate_pack.py +++ b/scripts/generate_pack.py @@ -2314,6 +2314,39 @@ def generate_target_manifests(targets_dir: str, output_dir: str) -> None: print(f"Generated {count} target manifest(s) in {output_dir}") +def _refuse_unknown_systems( + system_filter: list[str], + platforms: list[str], + platforms_dir: str, + parser: argparse.ArgumentParser, +) -> None: + """Refuse a --system no selected platform declares, before any work. + + The name is known once the config is loaded. Checked inside the build, a + typo cost the lock and a refresh of every data directory first, then + ended on a WARNING and exit 1. + """ + from common import _norm_system_id + + wanted = {_norm_system_id(s) for s in system_filter} + declared: set[str] = set() + for name in platforms: + systems = load_platform_config(name, platforms_dir).get("systems", {}) + if any(sid in system_filter or _norm_system_id(sid) in wanted for sid in systems): + return + declared.update(systems) + where = f"--platform {platforms[0]}" if len(platforms) == 1 else "--all" + hint = suggest_names( + system_filter, + sorted(declared), + f"--platform {platforms[0]} --list-systems" if len(platforms) == 1 + else "--platform --list-systems", + ) + parser.error( + f"--system {','.join(system_filter)} matches no system of {where}. {hint}" + ) + + def _refuse_for_verify_packs(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None: """What --verify-packs cannot be combined with, refused before any lock. @@ -3332,6 +3365,9 @@ def _main(held: contextlib.ExitStack) -> None: parser.error("Specify --platform or --all") return + if system_filter: + _refuse_unknown_systems(system_filter, platforms, args.platforms_dir, parser) + data_registry = load_data_dir_registry(args.platforms_dir) # The output directory is claimed before the data directory refresh: a # refresh waits for every run that holds a cache, so a second build diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 54291e50..bc680448 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -6296,6 +6296,34 @@ class ATargetTypoIsAnErrorInEveryTool(unittest.TestCase): self.assertIn("verify.py --platform retroarch --list-targets", output) +class AnUnknownSystemIsRefusedBeforeAnyWork(unittest.TestCase): + """generate_pack checked --system inside the build: a typo took the + output lock and refreshed every data directory, then ended on a WARNING.""" + + def test_refused_before_the_lock_and_the_refresh(self): + with tempfile.TemporaryDirectory(dir=ROOT / "tmp") as tmp: + out = Path(tmp) / "never" + with mock.patch.dict(os.environ, {"RETROBIOS_OFFLINE": ""}): + proc = subprocess.run( + [sys.executable, "scripts/generate_pack.py", "--platform", "batocera", + "--system", "playstaton-nothing", "--output-dir", str(out)], + cwd=ROOT, capture_output=True, text=True, timeout=120, + ) + self.assertEqual(proc.returncode, 2, proc.stdout + proc.stderr) + self.assertIn("error: --system playstaton-nothing matches no system", proc.stderr) + self.assertFalse(out.exists()) + + def test_a_known_system_passes_the_early_check(self): + import argparse + + import generate_pack + + parser = argparse.ArgumentParser() + generate_pack._refuse_unknown_systems( + ["sony-playstation"], ["retroarch"], str(ROOT / "platforms"), parser + ) + + class NoArchiveRepeatsAMemberName(unittest.TestCase): """s8k_cpu.zip held three member names twice (MAME reuses them across two ROM_SYSTEM_BIOS of one device). Every pack build printed raw zipfile