diff --git a/scripts/generate_pack.py b/scripts/generate_pack.py index e1ddb796..a541cf54 100644 --- a/scripts/generate_pack.py +++ b/scripts/generate_pack.py @@ -2237,8 +2237,10 @@ def _run_manifest_mode( for source, required_only in variants: for group_platforms, representative in groups: print(f"\nGenerating manifest for {representative} [source={source}]...") + tc = target_cores_cache.get(representative) if args.target else None + if _skip_empty_truth(args, source, representative, emu_profiles, tc, failed): + continue try: - tc = target_cores_cache.get(representative) if args.target else None manifest = generate_manifest( representative, args.platforms_dir, @@ -2403,6 +2405,37 @@ def _pack_label(source: str, required_only: bool) -> str: return f"[source={source}, required]" if required_only else f"[source={source}]" +def _truth_layer_is_empty( + platform: str, platforms_dir: str, emu_profiles: dict, target_cores +) -> bool: + """Whether a platform runs no core a profile describes. + + MiSTer declares `cores: []`: its truth pack held nothing, passed its + check and was published under a name claiming a content. + """ + config = load_platform_config(platform, platforms_dir) + return not _platform_cores(config, emu_profiles or {}, target_cores=target_cores) + + +def _skip_empty_truth(args, source: str, representative: str, emu_profiles, tc, + failed: list[str]) -> bool: + """Leave out a truth pack that would be empty; refuse it when asked for.""" + if source != "truth" or not _truth_layer_is_empty( + representative, args.platforms_dir, emu_profiles, tc + ): + return False + if args.all_variants: + print(f" {representative}: no core profile, no truth pack") + else: + print( + f"ERROR: {representative} runs no core a profile describes; " + "a truth pack would hold nothing", + file=sys.stderr, + ) + failed.append(representative) + return True + + def _produced(split: bool, zip_path, zip_paths) -> bool: """Whether the build wrote what the run asked for.""" return bool(zip_paths if split else zip_path) @@ -2444,8 +2477,10 @@ def _run_platform_packs( zip_path = None zip_paths: list = [] + tc = target_cores_cache.get(representative) if args.target else None + if _skip_empty_truth(args, source, representative, emu_profiles, tc, failed): + continue try: - tc = target_cores_cache.get(representative) if args.target else None if args.split: zip_paths = generate_split_packs( representative, diff --git a/tests/test_e2e.py b/tests/test_e2e.py index a1b7d4d5..5b545399 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -6157,6 +6157,24 @@ struct BurnDriver BurnDrvneogeo = { "a required-only system pack took the name of the full one", ) + def test_a_platform_without_cores_has_no_truth_pack(self): + """MiSTer declares cores: []: its truth pack held nothing, passed + its check and exited 0 under a name claiming a content.""" + import subprocess + + repo = os.path.join(os.path.dirname(__file__), "..") + out = os.path.join(self.root, "truth_empty") + proc = subprocess.run( + [sys.executable, "scripts/generate_pack.py", "--platform", "misterfpga", + "--offline", "--source", "truth", "--output-dir", out], + capture_output=True, text=True, cwd=repo, timeout=300, + ) + self.assertNotEqual(proc.returncode, 0) + self.assertIn("a truth pack would hold nothing", proc.stderr) + self.assertFalse( + [f for f in os.listdir(out) if f.endswith(".zip")] if os.path.isdir(out) else [] + ) + def test_a_full_pack_is_not_announced_as_narrowed(self): from generate_pack import _build_readme, _narrowings