mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-18 01:13:30 -05:00
fix: replay absent core claims when narrowing
This commit is contained in:
1 parent
20bdd381ec
commit
0bac022c2b
5 files changed
+88
-18
No files matched your search
+1
-1
@@ -8,7 +8,7 @@ param(
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "61ffed8fddf816c02c1b45268dfd9bfdff9daeb1da2adedf2e13d14472f8654d"
|
||||
$defaultInstallSha256 = "2e9e93d9809d9276a84209cfd421ad9b0d55853ce9c9d6761beb2ef21044ef2f"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+43
-15
@@ -1151,6 +1151,27 @@ _COPY_FIELDS = (
|
||||
)
|
||||
|
||||
|
||||
def _check_omission(entry: dict, dest: str) -> None:
|
||||
"""The fields of a file left out: an omitted entry, or the claim of a core
|
||||
with nothing held at a destination another core's copy serves."""
|
||||
if not isinstance(entry.get("name"), str) or not entry["name"]:
|
||||
raise ValueError(f"invalid omitted file name for {dest}")
|
||||
if not isinstance(entry.get("system", ""), str):
|
||||
raise ValueError(f"invalid omitted system for {dest}")
|
||||
if not isinstance(entry.get("required"), bool):
|
||||
raise ValueError(f"invalid omitted required flag for {dest}")
|
||||
if entry.get("reason") not in OMISSION_REASONS:
|
||||
raise ValueError(f"invalid omission reason for {dest}")
|
||||
cores = entry.get("cores")
|
||||
if cores is not None and (
|
||||
not isinstance(cores, list)
|
||||
or not all(isinstance(core, str) for core in cores)
|
||||
):
|
||||
raise ValueError(f"invalid omitted cores list for {dest}")
|
||||
for field in SELECTION_FIELDS:
|
||||
_check_tags(entry, field, dest)
|
||||
|
||||
|
||||
def _check_download(entry: dict, where: str, dest: str) -> int:
|
||||
"""The size, hashes, source and cores of one downloadable copy; its size."""
|
||||
size = entry.get("size")
|
||||
@@ -1271,6 +1292,13 @@ def _validate_manifest(data: object, plat: str) -> dict:
|
||||
# Narrowing can swap in any copy: the cap holds for the largest.
|
||||
size = max(size, _check_download(alternative, where, dest))
|
||||
size = max(size, _check_slot(entry, f"files[{index}]", dest))
|
||||
absent = entry.get("absent_claims", [])
|
||||
if not isinstance(absent, list) or len(absent) > MAX_SELECTION_TAGS:
|
||||
raise ValueError(f"invalid absent claims for {dest}")
|
||||
for claim in absent:
|
||||
if not isinstance(claim, dict):
|
||||
raise ValueError(f"invalid absent claim for {dest}")
|
||||
_check_omission(claim, dest)
|
||||
largest_total += size
|
||||
if largest_total > MAX_TOTAL_DOWNLOAD_SIZE:
|
||||
raise ValueError("manifest total size exceeds safety limit")
|
||||
@@ -1299,20 +1327,7 @@ def _validate_manifest(data: object, plat: str) -> dict:
|
||||
if dest in seen_destinations or dest in seen_omitted:
|
||||
raise ValueError(f"duplicate or conflicting omitted destination: {dest}")
|
||||
seen_omitted.add(dest)
|
||||
if not isinstance(entry.get("name"), str) or not entry["name"]:
|
||||
raise ValueError(f"invalid omitted file name for {dest}")
|
||||
if not isinstance(entry.get("system", ""), str):
|
||||
raise ValueError(f"invalid omitted system for {dest}")
|
||||
if not isinstance(entry.get("required"), bool):
|
||||
raise ValueError(f"invalid omitted required flag for {dest}")
|
||||
if entry.get("reason") not in OMISSION_REASONS:
|
||||
raise ValueError(f"invalid omission reason for {dest}")
|
||||
cores = entry.get("cores")
|
||||
if cores is not None and (
|
||||
not isinstance(cores, list)
|
||||
or not all(isinstance(core, str) for core in cores)
|
||||
):
|
||||
raise ValueError(f"invalid omitted cores list for {dest}")
|
||||
_check_omission(entry, dest)
|
||||
claims = entry.get("alternatives", [])
|
||||
if not isinstance(claims, list) or len(claims) > MAX_SELECTION_TAGS:
|
||||
raise ValueError(f"invalid omitted alternatives for {dest}")
|
||||
@@ -1566,6 +1581,8 @@ def narrow_to_cores(
|
||||
def kept_system(system: str) -> bool:
|
||||
return system not in reach or bool(cores.intersection(reach[system]))
|
||||
|
||||
displaced: list[dict] = []
|
||||
|
||||
def kept(entries: list[dict]) -> list[dict]:
|
||||
result: list[dict] = []
|
||||
for entry in entries:
|
||||
@@ -1573,6 +1590,16 @@ def narrow_to_cores(
|
||||
copy = _for_cores(entry, cores)
|
||||
if copy is not None:
|
||||
result.append(copy)
|
||||
continue
|
||||
# None of the cores served here is named: a named core that
|
||||
# wants the slot and has nothing held is a file left out.
|
||||
claim = next(
|
||||
(c for c in entry.get("absent_claims", [])
|
||||
if cores.intersection(c.get("cores") or [])),
|
||||
None,
|
||||
)
|
||||
if claim is not None:
|
||||
displaced.append({"dest": entry["dest"], **claim})
|
||||
continue
|
||||
# The systems declaring the file, where they are fewer than the
|
||||
# ones reading it.
|
||||
@@ -1589,7 +1616,8 @@ def narrow_to_cores(
|
||||
|
||||
files = manifest.get("files", []) if files is None else files
|
||||
omitted = manifest.get("omitted_files", []) if omitted is None else omitted
|
||||
return kept(files), kept(omitted)
|
||||
kept_files = kept(files)
|
||||
return kept_files, kept(omitted) + displaced
|
||||
|
||||
|
||||
def canonical_region(raw: str) -> str:
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="61ffed8fddf816c02c1b45268dfd9bfdff9daeb1da2adedf2e13d14472f8654d"
|
||||
DEFAULT_INSTALL_SHA256="2e9e93d9809d9276a84209cfd421ad9b0d55853ce9c9d6761beb2ef21044ef2f"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -3299,6 +3299,20 @@ def _attach_claims(
|
||||
dest_hint=claim.get("destination", ""), offline=offline,
|
||||
)
|
||||
if claim_status in ("not_found", "external", "user_provided") or not claim_path:
|
||||
# Nothing held for this core: narrowed to it without the cores
|
||||
# served above, the slot is a file left out (installer replays it).
|
||||
systems = _extra_system_ids(claim)
|
||||
entry.setdefault("absent_claims", []).append({
|
||||
"name": str(claim.get("name") or ""),
|
||||
"system": systems[0] if systems else "",
|
||||
"required": bool(claim.get("required", True)),
|
||||
"reason": (
|
||||
claim_status
|
||||
if claim_status in ("not_found", "external", "user_provided")
|
||||
else "not_found"
|
||||
),
|
||||
"cores": [claim_emu],
|
||||
})
|
||||
continue
|
||||
claim_path, _ = validated_choice(
|
||||
claim, claim_path, db, validation_index or {}, bios_dir, None, dest, owners,
|
||||
@@ -3495,7 +3509,14 @@ def _manifest_core_entries(
|
||||
)
|
||||
|
||||
manifest_files.append(entry)
|
||||
omitted_by_destination.pop(full_dest, None)
|
||||
displaced = omitted_by_destination.pop(full_dest, None)
|
||||
if displaced and displaced.get("cores"):
|
||||
# A core that wants this slot and has nothing held: a pack built
|
||||
# without the cores served here leaves the slot to it, missing,
|
||||
# and the installer narrowing to those cores has to say so too.
|
||||
entry.setdefault("absent_claims", []).append(
|
||||
{k: v for k, v in displaced.items() if k != "dest"}
|
||||
)
|
||||
total_size += file_size
|
||||
seen_destinations.add(full_dest)
|
||||
_register_path(full_dest, seen_destinations, seen_parents)
|
||||
|
||||
@@ -630,5 +630,26 @@ class InstallMatchesATargetManifest(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
|
||||
class ACoreWithNothingHeldIsAnOmission(unittest.TestCase):
|
||||
"""DOSBox serves SC-55/WAVEROM3.BIN, DOSBox Pure wants it and nothing is
|
||||
held for it. A pack built for a target without DOSBox lists it as left
|
||||
out; narrowed from the full manifest, it had vanished."""
|
||||
|
||||
MANIFEST = {"files": [{
|
||||
"dest": "SC-55/WAVEROM3.BIN", "cores": ["dosbox"], "sha1": "1" * 40,
|
||||
"absent_claims": [{"name": "WAVEROM3.BIN", "system": "dos", "required": False,
|
||||
"reason": "not_found", "cores": ["dosbox_pure"]}],
|
||||
}], "omitted_files": []}
|
||||
|
||||
def test_narrowing_away_the_holder_leaves_the_omission(self):
|
||||
files, omitted = install.narrow_to_cores(self.MANIFEST, ["dosbox_pure"])
|
||||
self.assertEqual(files, [])
|
||||
self.assertEqual([o["dest"] for o in omitted], ["SC-55/WAVEROM3.BIN"])
|
||||
files, omitted = install.narrow_to_cores(self.MANIFEST, ["dosbox", "dosbox_pure"])
|
||||
self.assertEqual(len(files), 1)
|
||||
self.assertEqual(omitted, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user