mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 12:49:03 -05:00
fix: compare pack paths, not only counts
Step 5 compared (ok, total) and the core file count. RetroArch, Lakka and RetroPie read 526/526 on both sides while verify credited four paths below SGB1.sfc and SGB2.sfc that the pack never writes: a declared file blocks them as a directory. verify now places the platform files in the builder's order with the same path rules, and a user_provided or external declaration keeps its slot on both sides, so no core file takes it in the report only. verify --destinations writes the paths each pack holds by the report, and step 5 compares them with the archive members (notes and data tree members set aside). Every emulator profile, in each build it has, is built in memory (PackRecord, nothing read or written) and compared with verify --emulator path by path. Cost measured here: about 1 s per platform archive and 11 s for the 930 emulator packs.
This commit is contained in:
1 parent
fb639647e6
commit
38af19091d
8 files changed
+509
-25
No files matched your search
@@ -106,6 +106,20 @@ DEFAULT_BIOS_DIR = "bios"
|
||||
|
||||
|
||||
|
||||
class PackRecord:
|
||||
"""A pack build kept in memory: the paths it writes, no archive.
|
||||
|
||||
``members`` is every path written, ``files`` the ones the build counts as
|
||||
the profile's files (archives and loose files, not trees or notes). The
|
||||
pipeline builds every emulator pack this way and compares ``files`` with
|
||||
what verify --emulator credits, without reading or compressing a byte.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.members: list[str] = []
|
||||
self.files: list[str] = []
|
||||
|
||||
|
||||
def _add_pack_member(zf: zipfile.ZipFile, source_path: str, arcname: str) -> None:
|
||||
"""Add a file to the pack with metadata that does not depend on the build.
|
||||
|
||||
@@ -117,6 +131,9 @@ def _add_pack_member(zf: zipfile.ZipFile, source_path: str, arcname: str) -> Non
|
||||
two consecutive builds. The content is streamed rather than read whole,
|
||||
because some members are firmware images of several hundred megabytes.
|
||||
"""
|
||||
if isinstance(zf, PackRecord):
|
||||
zf.members.append(arcname)
|
||||
return
|
||||
info = zipfile.ZipInfo(filename=arcname, date_time=_FIXED_DATE_TIME)
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
# The executable bit is the one permission worth carrying: the RetroDECK
|
||||
@@ -143,6 +160,9 @@ def _write_generated_member(zf: zipfile.ZipFile, arcname: str, text: str) -> Non
|
||||
a different hash on every run. Pinning them to the epoch the archive
|
||||
rebuilder already uses makes a pack a function of its inputs alone.
|
||||
"""
|
||||
if isinstance(zf, PackRecord):
|
||||
zf.members.append(arcname)
|
||||
return
|
||||
info = zipfile.ZipInfo(filename=arcname, date_time=_FIXED_DATE_TIME)
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o100644 << 16
|
||||
@@ -895,6 +915,13 @@ def generate_pack(
|
||||
) as tmp:
|
||||
tmp_path = tmp.name
|
||||
|
||||
# The slot is the platform's file whether the download
|
||||
# arrives or not: verify cannot fetch, and a core file
|
||||
# filling it on a failed download made the two differ.
|
||||
seen_destinations.add(dedup_key)
|
||||
_register_path(dedup_key, seen_destinations, seen_parents)
|
||||
if case_insensitive:
|
||||
seen_lower.add(dedup_key.lower())
|
||||
try:
|
||||
if download_external(file_entry, tmp_path):
|
||||
extract = file_entry.get("extract", False)
|
||||
@@ -902,10 +929,6 @@ def generate_pack(
|
||||
_extract_zip_to_archive(tmp_path, _flat(full_dest, base_dest, flatten), zf)
|
||||
else:
|
||||
_add_pack_member(zf, tmp_path, _flat(full_dest, base_dest, flatten))
|
||||
seen_destinations.add(dedup_key)
|
||||
_register_path(dedup_key, seen_destinations, seen_parents)
|
||||
if case_insensitive:
|
||||
seen_lower.add(dedup_key.lower())
|
||||
file_status.setdefault(dedup_key, "ok")
|
||||
total_files += 1
|
||||
else:
|
||||
@@ -1232,6 +1255,9 @@ def _add_zip_to_pack(
|
||||
declare the hash of a ROM inside the ZIP (checkInsideZip), which a
|
||||
content-preserving rebuild does not affect.
|
||||
"""
|
||||
if isinstance(target_zf, PackRecord):
|
||||
target_zf.members.append(dest_path)
|
||||
return
|
||||
if (
|
||||
file_entry
|
||||
and not file_entry.get("zipped_file")
|
||||
@@ -1379,6 +1405,7 @@ def generate_emulator_pack(
|
||||
pack_name: str | None = None,
|
||||
system_ids: list[str] | None = None,
|
||||
final_dir: str | None = None,
|
||||
record: PackRecord | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for specific emulator profiles.
|
||||
|
||||
@@ -1387,6 +1414,8 @@ def generate_emulator_pack(
|
||||
the 255 bytes a file name may hold. `system_ids` keeps only the entries
|
||||
of those systems, as a platform pack narrows core extras. `final_dir`
|
||||
is where the caller moves the archive, the path the report names.
|
||||
`record` builds the pack in memory instead (PackRecord): nothing is
|
||||
written or read, and the paths are kept on it.
|
||||
"""
|
||||
all_profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||
if zip_contents is None:
|
||||
@@ -1411,7 +1440,8 @@ def generate_emulator_pack(
|
||||
display_names, [n for n, _p in selected], f"{narrow_tags}_BIOS_Pack.zip"
|
||||
)
|
||||
zip_path = os.path.join(output_dir, zip_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
if record is None:
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
region_drops: set[str] = (
|
||||
emulator_region_drops(selected, standalone, regions, required_only, db)
|
||||
@@ -1449,7 +1479,10 @@ def generate_emulator_pack(
|
||||
platforms_dir or os.path.join(os.path.dirname(__file__), "..", "platforms")
|
||||
)
|
||||
|
||||
with _writing_pack(zip_path) as zf:
|
||||
with (
|
||||
contextlib.nullcontext(record) if record is not None
|
||||
else _writing_pack(zip_path)
|
||||
) as zf:
|
||||
for emu_name, profile in sorted(selected):
|
||||
pack_structure = profile.get("pack_structure")
|
||||
files = expand_agnostic_entries(
|
||||
@@ -1514,6 +1547,8 @@ def generate_emulator_pack(
|
||||
seen_lower.add(archive_dest.lower())
|
||||
packed_at[archive_dest.lower()] = local_path
|
||||
total_files += 1
|
||||
if record is not None:
|
||||
record.files.append(archive_dest)
|
||||
else:
|
||||
report_missing(archive_name, archive_dest)
|
||||
|
||||
@@ -1641,12 +1676,16 @@ def generate_emulator_pack(
|
||||
) as tmp:
|
||||
tmp_path = tmp.name
|
||||
try:
|
||||
if download_external(fe, tmp_path):
|
||||
# Built in memory, nothing is fetched: verify --emulator
|
||||
# cannot fetch either and reports the file missing.
|
||||
if record is None and download_external(fe, tmp_path):
|
||||
_add_pack_member(zf, tmp_path, dest)
|
||||
seen_destinations.add(dest)
|
||||
_register_path(dest, seen_destinations, seen_parents)
|
||||
seen_lower.add(dest.lower())
|
||||
total_files += 1
|
||||
if record is not None:
|
||||
record.files.append(dest)
|
||||
else:
|
||||
missing_files.append(fe["name"])
|
||||
finally:
|
||||
@@ -1675,6 +1714,8 @@ def generate_emulator_pack(
|
||||
seen_lower.add(dest.lower())
|
||||
packed_at[dest.lower()] = local_path
|
||||
total_files += 1
|
||||
if record is not None:
|
||||
record.files.append(dest)
|
||||
|
||||
# The trees after the files, as in a platform pack: a dump the
|
||||
# profile declares wins over the copy a tree bundles at the same
|
||||
@@ -1705,7 +1746,9 @@ def generate_emulator_pack(
|
||||
)
|
||||
|
||||
# Remove empty ZIP (no files packed and no missing = nothing to ship)
|
||||
if total_files + data_files == 0 and not (missing_files or rejected_files or instruction_files or unsourceable_files):
|
||||
if record is None and total_files + data_files == 0 and not (
|
||||
missing_files or rejected_files or instruction_files or unsourceable_files
|
||||
):
|
||||
os.unlink(zip_path)
|
||||
|
||||
# Report. The members of data trees are counted apart: verify --emulator
|
||||
|
||||
Reference in new issue
Block a user