From 17e35d1af9eb92576cf4959cbe63d7c0466312e8 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Tue, 20 Oct 2026 04:56:23 +0200 Subject: [PATCH] feat: file cards name the packs that install them A card named only the platforms whose own list declares the file. A RetroBat user looking up scph5501.bin found every platform but RetroBat, whose pack installs it for six cores. The cards now read the install manifests and add each pack that places the file, with the destination it gives it. --- scripts/cross_reference.py | 19 +++++++++++++++++++ scripts/generate_site.py | 19 +++++++++++++++++++ tests/test_site_exports.py | 24 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/scripts/cross_reference.py b/scripts/cross_reference.py index e8d03340..5ba4cc83 100644 --- a/scripts/cross_reference.py +++ b/scripts/cross_reference.py @@ -341,6 +341,9 @@ class FileConsumers: # Where each platform puts the file in its BIOS directory. self.platform_destinations: dict[str, dict[str, set[str]]] = {} self.emulators: dict[str, set[str]] = {} + # Where each platform's pack installs the file, its cores' files + # included: the platform's own list names a fraction of them. + self.installed: dict[str, dict[str, set[str]]] = {} def sha1_of(self, entry: dict, dest: str = "", owner: str = "") -> str | None: """The collected file an entry resolves to, or None. @@ -378,6 +381,22 @@ class FileConsumers: name, set() ).add(where) + def add_manifests(self, install_dir: str | Path) -> None: + """Read what each install manifest places, alternatives included.""" + for path in sorted(Path(install_dir).glob("*.json")): + try: + manifest = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + print(f"WARNING: {path}: {exc}", file=sys.stderr) + continue + platform = str(manifest.get("platform") or path.stem) + for entry in manifest.get("files") or []: + for sha1 in {entry.get("sha1"), *( + alt.get("sha1") for alt in entry.get("alternatives") or [] + )} - {None, ""}: + self.installed.setdefault(sha1, {}).setdefault( + platform, set()).add(str(entry.get("dest", ""))) + def path_of(self, sha1: str) -> str: """The collection path the database records for a file.""" return str(self._db.get("files", {}).get(sha1, {}).get("path", "")) diff --git a/scripts/generate_site.py b/scripts/generate_site.py index 2ee9a1b7..d2252d92 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -2325,6 +2325,18 @@ def _platform_links(sha1: str, consumers: FileConsumers) -> list[str]: return links +def _installed_links(sha1: str, consumers: FileConsumers, declared: set[str]) -> list[str]: + """Each pack installing the file that its platform's list does not name, + with the place the pack gives it.""" + links = [] + for platform, dests in sorted(getattr(consumers, "installed", {}).get(sha1, {}).items()): + if platform in declared: + continue + where = ", ".join(_code(d) for d in sorted(dests) if d) + links.append(f"[{platform}](../platforms/{platform}.md)" + (f" {where}" if where else "")) + return links + + def _system_file_card(f: dict, consumers: FileConsumers) -> list[str]: """One file of a system page: hashes, who reads it, its provenance.""" name = f["name"] @@ -2364,6 +2376,12 @@ def _system_file_card(f: dict, consumers: FileConsumers) -> list[str]: # that platform puts the file: from the card, retrodeck sent the # reader to the top of a 2,000-file page. lines.append(f"- Platforms: {', '.join(_platform_links(sha1_full, consumers))}") + installed = _installed_links(sha1_full, consumers, set(plats)) + if installed: + # The packs that carry the file for their cores: a RetroBat user + # looking up scph5501.bin found every platform but RetroBat, whose + # pack installs it for six cores. + lines.append(f"- Also installed by: {', '.join(installed)}") if emus: lines.append(f"- Emulators: {', '.join(_emulator_link(e, '../') for e in emus)}") provenance = f.get("provenance", {}) @@ -4117,6 +4135,7 @@ def _build_file_consumers(db: dict, coverages: dict, profiles: dict) -> FileCons consumers = FileConsumers(db, build_zip_contents_index(db)) consumers.add_platforms({name: cov["config"] for name, cov in coverages.items()}) consumers.add_emulators(profiles) + consumers.add_manifests(Path(__file__).resolve().parent.parent / "install") return consumers diff --git a/tests/test_site_exports.py b/tests/test_site_exports.py index 3d014742..ab331ff1 100644 --- a/tests/test_site_exports.py +++ b/tests/test_site_exports.py @@ -678,5 +678,29 @@ class AFileHasAnAddressByContent(unittest.TestCase): self.assertIn("../../files/?sha1=" + "ab" * 20, card) +class ACardNamesThePacksThatInstallIt(unittest.TestCase): + """A file card named only the platforms whose own list declares the + file: a RetroBat user found no RetroBat on scph5501.bin, which its pack + installs for six cores.""" + + def test_a_manifest_destination_reaches_the_card(self): + import json as _json + + from cross_reference import FileConsumers + from generate_site import _installed_links + + with tempfile.TemporaryDirectory(dir=ROOT / "tmp") as tmp: + Path(tmp, "retrobat.json").write_text(_json.dumps({ + "platform": "retrobat", + "files": [{"dest": "scph5501.bin", "sha1": "b" * 40, + "alternatives": [{"sha1": "a" * 40}]}], + })) + consumers = FileConsumers({"files": {}}) + consumers.add_manifests(tmp) + links = _installed_links("a" * 40, consumers, set()) + self.assertEqual(links, ["[retrobat](../platforms/retrobat.md) `scph5501.bin`"]) + self.assertEqual(_installed_links("a" * 40, consumers, {"retrobat"}), []) + + if __name__ == "__main__": unittest.main()