mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-20 10:23:16 -05:00
A file installed with its emulator is already on the user's machine and the packs leave it out. bundled_only.py lists the collected copies nothing else needs: no platform declares them, no entry of any build needs them, no install manifest ships them.
112 lines
4.4 KiB
Python
112 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Collected files that only an emulator's own install provides.
|
|
|
|
A file installed with the emulator is already on the user's machine, and the
|
|
packs leave it out. A copy in bios/ that nothing else asks for is then dead
|
|
weight: no platform declares it, no profile entry of any build needs it from
|
|
the collection, and no install manifest ships it. This lists those copies, so
|
|
they can be removed, and fails under --strict while one remains.
|
|
|
|
Usage:
|
|
python scripts/bundled_only.py
|
|
python scripts/bundled_only.py --strict
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from common import (
|
|
expand_directory_entries,
|
|
list_registered_platforms,
|
|
load_database,
|
|
load_emulator_profiles,
|
|
load_platform_config,
|
|
)
|
|
from cross_reference import FileConsumers
|
|
|
|
|
|
def _entries(profile: dict, db: dict) -> list[dict]:
|
|
files = [f for f in profile.get("files") or [] if isinstance(f, dict)]
|
|
return expand_directory_entries(files, db, False) + [
|
|
f for f in expand_directory_entries(files, db, True) if f not in files
|
|
]
|
|
|
|
|
|
def shipped_by_manifests(install_dir: Path) -> set[str]:
|
|
"""Every sha1 an install manifest places, alternatives included."""
|
|
shipped: set[str] = set()
|
|
for path in sorted(install_dir.glob("*.json")):
|
|
manifest = json.loads(path.read_text(encoding="utf-8"))
|
|
for entry in manifest.get("files") or []:
|
|
shipped.add(str(entry.get("sha1") or ""))
|
|
shipped.update(str(a.get("sha1") or "") for a in entry.get("alternatives") or [])
|
|
return shipped - {""}
|
|
|
|
|
|
def bundled_only(db: dict, profiles: dict, platforms_dir: str, install_dir: Path) -> list[dict]:
|
|
"""Collected files a bundled entry resolves to and nothing else needs."""
|
|
consumers = FileConsumers(db)
|
|
needed: set[str] = set(shipped_by_manifests(install_dir))
|
|
for name in list_registered_platforms(platforms_dir, include_archived=True):
|
|
config = load_platform_config(name, platforms_dir)
|
|
for system in config.get("systems", {}).values():
|
|
for entry in system.get("files", []):
|
|
sha1 = consumers.sha1_of(entry, entry.get("destination", ""))
|
|
if sha1:
|
|
needed.add(sha1)
|
|
bundled: dict[str, set[str]] = {}
|
|
for emulator, profile in profiles.items():
|
|
if profile.get("type") == "alias":
|
|
continue
|
|
for entry in _entries(profile, db):
|
|
sha1 = consumers.sha1_of(entry, entry.get("path") or "", owner=emulator)
|
|
if not sha1:
|
|
continue
|
|
if entry.get("bundled"):
|
|
bundled.setdefault(sha1, set()).add(emulator)
|
|
else:
|
|
needed.add(sha1)
|
|
files = db.get("files", {})
|
|
return [
|
|
{"sha1": sha1, "path": files[sha1]["path"], "size": files[sha1].get("size", 0),
|
|
"emulators": sorted(emulators)}
|
|
for sha1, emulators in sorted(bundled.items(), key=lambda kv: files[kv[0]]["path"])
|
|
if sha1 not in needed and sha1 in files
|
|
]
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(allow_abbrev=False, description=__doc__.splitlines()[0])
|
|
parser.add_argument("--db", default="database.json", help="database to read (default: %(default)s)")
|
|
parser.add_argument("--emulators-dir", default="emulators", help="profiles (default: %(default)s)")
|
|
parser.add_argument("--platforms-dir", default="platforms", help="platform lists (default: %(default)s)")
|
|
parser.add_argument("--install-dir", default="install", type=Path,
|
|
help="install manifests, whose files are shipped (default: %(default)s)")
|
|
parser.add_argument("--json", action="store_true", help="print the list as JSON")
|
|
parser.add_argument("--strict", action="store_true", help="exit 1 while a file is listed")
|
|
args = parser.parse_args()
|
|
|
|
rows = bundled_only(
|
|
load_database(args.db), load_emulator_profiles(args.emulators_dir),
|
|
args.platforms_dir, args.install_dir,
|
|
)
|
|
if args.json:
|
|
print(json.dumps(rows, indent=2))
|
|
else:
|
|
for row in rows:
|
|
print(f"{row['path']} ({', '.join(row['emulators'])})")
|
|
total = sum(row["size"] for row in rows)
|
|
print(f"{len(rows)} collected files only an emulator install provides, "
|
|
f"{total / 1024**2:.1f} MB")
|
|
return 1 if args.strict and rows else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from common import run_cli
|
|
|
|
run_cli(main)
|