mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-16 08:23:28 -05:00
fix: keep a platform omission behind core claims
This commit is contained in:
1 parent
a8b9232093
commit
0ddeb2afa4
6 files changed
+144
-16
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 = "bbc1b4665a37255deecf941765a1c13bb271da54ecf626bd218747165a126820"
|
||||
$defaultInstallSha256 = "f9e46dd53fc7501ddf18ece3fc7f08dbf738c617fec93707c6a7f40e8db39673"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
$temporary = $null
|
||||
|
||||
+14
-1
@@ -1234,7 +1234,20 @@ def _validate_manifest(data: object, plat: str) -> dict:
|
||||
or not all(isinstance(core, str) for core in cores)
|
||||
):
|
||||
raise ValueError(f"invalid omitted cores list for {dest}")
|
||||
for field in SELECTION_FIELDS:
|
||||
claims = entry.get("alternatives", [])
|
||||
if not isinstance(claims, list) or len(claims) > MAX_SELECTION_TAGS:
|
||||
raise ValueError(f"invalid omitted alternatives for {dest}")
|
||||
for claim in claims:
|
||||
claim_cores = claim.get("cores") if isinstance(claim, dict) else None
|
||||
if (
|
||||
not isinstance(claim, dict)
|
||||
or set(claim) != {"cores"}
|
||||
or not isinstance(claim_cores, list)
|
||||
or not claim_cores
|
||||
or not all(isinstance(core, str) for core in claim_cores)
|
||||
):
|
||||
raise ValueError(f"invalid omitted alternative for {dest}")
|
||||
for field in (*SELECTION_FIELDS, "declared_in"):
|
||||
_check_tags(entry, field, dest)
|
||||
declared_total_omitted = data.get("total_omitted")
|
||||
if (
|
||||
|
||||
+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="bbc1b4665a37255deecf941765a1c13bb271da54ecf626bd218747165a126820"
|
||||
DEFAULT_INSTALL_SHA256="f9e46dd53fc7501ddf18ece3fc7f08dbf738c617fec93707c6a7f40e8db39673"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -257,6 +257,36 @@
|
||||
"priority": {
|
||||
"description": "The rank the code searches the file at, lower first: a world file withdraws an unmatched regional one only when it ranks no worse.",
|
||||
"type": "integer"
|
||||
},
|
||||
"declared_in": {
|
||||
"description": "The platform systems declaring the file, when fewer than `systems`, which also names the systems whose cores read it there.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"alternatives": {
|
||||
"description": "Cores claiming this destination behind the platform's own declaration, in the order the pack builder tries them; a narrowing that drops every declaring system takes the first whose cores remain.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"cores"
|
||||
],
|
||||
"properties": {
|
||||
"cores": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
-13
@@ -3231,14 +3231,17 @@ def _manifest_core_entries(
|
||||
)
|
||||
if status in ("not_found", "external", "user_provided") or not local_path:
|
||||
source_emu = fe.get("source_profile") or fe.get("source_emulator", "")
|
||||
systems = _extra_system_ids(fe)
|
||||
record_omission(
|
||||
full_dest,
|
||||
fe,
|
||||
systems[0] if systems else "",
|
||||
status,
|
||||
[source_emu] if source_emu else [],
|
||||
)
|
||||
if not _claim_platform_omission(
|
||||
omitted_by_destination.get(full_dest), source_emu
|
||||
):
|
||||
systems = _extra_system_ids(fe)
|
||||
record_omission(
|
||||
full_dest,
|
||||
fe,
|
||||
systems[0] if systems else "",
|
||||
status,
|
||||
[source_emu] if source_emu else [],
|
||||
)
|
||||
continue
|
||||
# The copy the emulator accepts, as the pack ships it.
|
||||
local_path, _disagreement = validated_choice(
|
||||
@@ -3259,11 +3262,14 @@ def _manifest_core_entries(
|
||||
selection.fields(dest, fe.get("name", "")),
|
||||
)
|
||||
if entry is None:
|
||||
systems = _extra_system_ids(fe)
|
||||
record_omission(
|
||||
full_dest, fe, systems[0] if systems else "", "not_found",
|
||||
[source_emu] if source_emu else [],
|
||||
)
|
||||
if not _claim_platform_omission(
|
||||
omitted_by_destination.get(full_dest), source_emu
|
||||
):
|
||||
systems = _extra_system_ids(fe)
|
||||
record_omission(
|
||||
full_dest, fe, systems[0] if systems else "", "not_found",
|
||||
[source_emu] if source_emu else [],
|
||||
)
|
||||
if file_size:
|
||||
pack_only_sizes.append(file_size)
|
||||
continue
|
||||
@@ -3285,6 +3291,23 @@ def _manifest_core_entries(
|
||||
return total_size
|
||||
|
||||
|
||||
def _claim_platform_omission(held: dict | None, emulator: str) -> bool:
|
||||
"""Record a core's claim on a destination the platform already omits.
|
||||
|
||||
The platform's own omission stands while one of its declaring systems
|
||||
does; a pack built without them falls to the first core claiming the
|
||||
path, as for a file present. Overwriting the record lost the platform's
|
||||
claim: a target without that core dropped an omission its pack reports.
|
||||
"""
|
||||
if held is None or held.get("cores") is not None:
|
||||
return False
|
||||
if emulator:
|
||||
claims = held.setdefault("alternatives", [])
|
||||
if {"cores": [emulator]} not in claims:
|
||||
claims.append({"cores": [emulator]})
|
||||
return True
|
||||
|
||||
|
||||
def _manifest_selection(
|
||||
config: dict,
|
||||
pack_systems: dict,
|
||||
@@ -3599,6 +3622,16 @@ def generate_manifest(
|
||||
entry, claims, db, bios_dir, zip_contents, offline, required_only,
|
||||
manifest_validation, manifest_owners, dest, dest, repo_root,
|
||||
)
|
||||
# A platform omission narrows like a platform file: by the systems
|
||||
# declaring it, which can be fewer than the ones whose cores read it.
|
||||
for full_dest, omission in omitted_by_destination.items():
|
||||
declared = declared_by.get(full_dest, set())
|
||||
if (
|
||||
omission.get("cores") is None
|
||||
and declared
|
||||
and declared != set(omission.get("systems") or [])
|
||||
):
|
||||
omission["declared_in"] = sorted(declared)
|
||||
|
||||
# Phase 3: data directories. The installer does not fetch them, so they
|
||||
# stay out of the file list; the pack carries them, so they count toward
|
||||
|
||||
@@ -192,6 +192,58 @@ class TheBoundaryBoundsAlternatives(unittest.TestCase):
|
||||
install._validate_manifest(manifest, "retroarch")
|
||||
|
||||
|
||||
class APlatformOmissionKeepsItsClaim(unittest.TestCase):
|
||||
"""A file the platform declares and a core reads, absent from the
|
||||
collection, was recorded under the core alone: RetroPie's dsi_nand.bin
|
||||
went to melonds, and a target without melonds lost the omission its
|
||||
pack reports for nintendo-ds."""
|
||||
|
||||
OMISSION = {
|
||||
"dest": "dsi_nand.bin", "name": "dsi_nand.bin", "system": "nintendo-ds",
|
||||
"required": True, "reason": "not_found", "cores": None,
|
||||
"systems": ["nintendo-ds", "nintendo-dsi"], "declared_in": ["nintendo-ds"],
|
||||
"alternatives": [{"cores": ["melonds"]}],
|
||||
}
|
||||
MANIFEST = {
|
||||
"files": [], "omitted_files": [OMISSION],
|
||||
"core_names": {"melonds": ["melonds"], "desmume": ["desmume"]},
|
||||
"system_cores": {"nintendo-ds": ["desmume"], "nintendo-dsi": ["melonds"]},
|
||||
}
|
||||
|
||||
def test_the_declaring_system_keeps_it(self):
|
||||
_, omitted = install.narrow_to_cores(self.MANIFEST, ["desmume"])
|
||||
self.assertEqual(omitted, [self.OMISSION])
|
||||
|
||||
def test_a_dropped_system_falls_to_the_claiming_core(self):
|
||||
_, omitted = install.narrow_to_cores(self.MANIFEST, ["melonds"])
|
||||
self.assertEqual([o["cores"] for o in omitted], [["melonds"]])
|
||||
_, omitted = install.narrow_to_cores(self.MANIFEST, ["mgba"])
|
||||
self.assertEqual(omitted, [])
|
||||
|
||||
def test_the_generator_files_the_core_behind_the_platform(self):
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
import generate_pack as gp
|
||||
|
||||
held = {"cores": None}
|
||||
self.assertTrue(gp._claim_platform_omission(held, "melonds"))
|
||||
self.assertTrue(gp._claim_platform_omission(held, "melonds"))
|
||||
self.assertEqual(held["alternatives"], [{"cores": ["melonds"]}])
|
||||
self.assertFalse(gp._claim_platform_omission({"cores": ["x"]}, "melonds"))
|
||||
self.assertFalse(gp._claim_platform_omission(None, "melonds"))
|
||||
|
||||
def test_the_boundary_takes_only_core_names(self):
|
||||
manifest = {"manifest_version": 2, "platform": "retroarch", "files": [],
|
||||
"omitted_files": [self.OMISSION]}
|
||||
install._validate_manifest(manifest, "retroarch")
|
||||
for claims in ([{"cores": []}], [{"cores": ["x"], "sha1": "a" * 40}],
|
||||
["melonds"], [5], {"cores": ["x"]}):
|
||||
bad = {**self.OMISSION, "alternatives": claims}
|
||||
with self.subTest(claims=claims), self.assertRaises(ValueError):
|
||||
install._validate_manifest(
|
||||
{**manifest, "omitted_files": [bad]}, "retroarch"
|
||||
)
|
||||
|
||||
|
||||
class RefusedBeforeAnyInstall(unittest.TestCase):
|
||||
def test_a_platform_shipping_its_cores_stops_the_run_first(self):
|
||||
tmp = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
||||
|
||||
Reference in new issue
Block a user