diff --git a/scripts/cross_reference.py b/scripts/cross_reference.py index fcc09057..5ddbfaff 100644 --- a/scripts/cross_reference.py +++ b/scripts/cross_reference.py @@ -608,6 +608,7 @@ def _cross_reference_profile( "name": fname, "path": effective_path, "required": f.get("required", False), + "bundled": bool(f.get("bundled")), "note": f.get("note", ""), "source_ref": f.get("source_ref", ""), "in_platform": False, diff --git a/scripts/generate_site.py b/scripts/generate_site.py index cbcb939b..82f02f52 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -653,7 +653,12 @@ def _gap_export_rows(coverages: dict, gap_report: dict | None = None) -> list[di "emulator": data.get("emulator", emu_key), "system": systems, "name": gap.get("name", ""), - "status": gap.get("source", ""), + # A file the emulator's own package installs is not one to + # find: its own status, as unsourceable has. + "status": ( + "bundled" if gap.get("bundled") and gap.get("source") == "missing" + else gap.get("source", "") + ), "required": bool(gap.get("required", False)), "in_repo": bool(gap.get("in_repo", False)), "reason": gap.get("note", ""), @@ -3109,10 +3114,18 @@ def generate_gap_analysis( src_missing_details: list[dict] = [] by_identity: dict[tuple[str, str], dict] = {} entries = 0 + bundled_rows: set[tuple[str, str]] = set() for emu_name, data in emulator_gaps: for g in data["gap_details"]: if g["source"] != "missing": continue + if g.get("bundled"): + # It arrives with the emulator's own package, which is + # why the collection documents it and does not hold it: + # asking readers to source it sent them after a file + # every install already has. + bundled_rows.add((data["emulator"], g["name"])) + continue entries += 1 key = (g["name"], g.get("identity") or f"emulator:{emu_name}") row = by_identity.get(key) @@ -3166,6 +3179,14 @@ def generate_gap_analysis( f"{_source_ref_markdown(profiles[m['emu_keys'][0]], m['source_ref'])} |" ) lines.append("") + if bundled_rows: + emulators = sorted({emulator for emulator, _name in bundled_rows}) + lines.extend([ + f"{len(bundled_rows)} further files arrive with their emulator's own " + f"package ({', '.join(emulators)}): documented in the profile, not " + "collected, and not listed here.", + "", + ]) # ---- Section 4: Acknowledged gaps (unsourceable files) ---- diff --git a/scripts/verify.py b/scripts/verify.py index c9350a56..9d04fea1 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -645,6 +645,7 @@ def find_undeclared_files( "path": dest, "required": f.get("required", False), "hle_fallback": f.get("hle_fallback", False), + "bundled": f.get("bundled", False), "category": f.get("category", "bios"), "in_repo": in_repo, "note": f.get("note", ""), @@ -1348,11 +1349,18 @@ def _split_undeclared(undeclared: list[dict]) -> tuple[list[dict], list[dict], l def _core_required_missing(bios_files: list[dict]) -> list[dict]: - """Core files a core cannot run without, absent from the collection.""" + """Core files a core cannot run without, absent from the collection. + + A file bundled with the emulator (DaedalusX64's n64.psh, the shader + library its renderer installs beside the binary) arrives with it: the + collection does not hold it by design, and listing it as missing sent + readers after a file every install already has. + """ return [ u for u in bios_files if u["required"] and not u["in_repo"] and not u.get("hle_fallback") + and not u.get("bundled") ] @@ -1374,6 +1382,11 @@ def _print_undeclared_section(result: dict, verbose: bool) -> None: opt_in_repo = [u for u in bios_files if not u["required"] and u["in_repo"]] opt_not_in_repo = [u for u in bios_files if not u["required"] and not u["in_repo"]] + bundled = [ + u for u in bios_files + if u["required"] and not u["in_repo"] and u.get("bundled") + and not u.get("hle_fallback") + ] core_in_pack = len(req_in_repo) + len(opt_in_repo) core_missing_req = len(req_not_in_repo) + len(req_hle_not_in_repo) core_missing_opt = len(opt_not_in_repo) @@ -1381,6 +1394,9 @@ def _print_undeclared_section(result: dict, verbose: bool) -> None: print( f" Core files: {core_in_pack} in pack, {core_missing_req} required missing, {core_missing_opt} optional missing" ) + if bundled: + names = ", ".join(sorted({u["name"] for u in bundled})) + print(f" Bundled with their emulator, not collected: {names}") if unplaced: print(f" Core files held but not packed: {len(unplaced)} ({unplaced[0]['omitted']})") diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index c50f892d..6c35d962 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -4421,5 +4421,35 @@ class OneOutputFormPerRun(unittest.TestCase): self.assertIn("not allowed with", proc.stderr) + +class BundledFilesAreNotToSource(unittest.TestCase): + """DaedalusX64's n64.psh, the shader library its renderer installs + beside the binary, was reported as a required core file missing and + listed among the files to source, with 19 more like it.""" + + def test_verify_does_not_count_it_missing(self): + sys.path.insert(0, str(ROOT / "scripts")) + import verify + + entry = {"name": "n64.psh", "required": True, "in_repo": False, + "category": "bios", "in_pack": True, "bundled": True} + self.assertEqual(verify._core_required_missing([entry]), []) + summary = verify._overall_summary( + [({"severity_counts": {}, "undeclared_files": [entry]}, ["ROCKNIX"])] + ) + self.assertNotIn("n64.psh", summary) + + def test_the_site_gives_it_its_own_status(self): + sys.path.insert(0, str(ROOT / "scripts")) + import generate_site + + rows = generate_site._gap_export_rows({}, {"daedalusx64": { + "emulator": "DaedalusX64", "systems": ["nintendo-64"], + "gap_details": [{"name": "n64.psh", "source": "missing", "required": True, + "in_repo": False, "bundled": True}], + }}) + self.assertEqual([row["status"] for row in rows], ["bundled"]) + + if __name__ == "__main__": unittest.main()