feat: allow --platform + --system combination

This commit is contained in:
Abdessamad Derraz
2026-03-28 00:36:51 +01:00
parent 020ff148c2
commit 43cb7a9884
3 changed files with 153 additions and 15 deletions

View File

@@ -1068,3 +1068,14 @@ def list_system_ids(emulators_dir: str) -> None:
for sys_id in sorted(system_emus):
count = len(system_emus[sys_id])
print(f" {sys_id:35s} ({count} emulator{'s' if count > 1 else ''})")
def list_platform_system_ids(platform_name: str, platforms_dir: str) -> None:
"""Print system IDs from a platform's YAML config."""
config = load_platform_config(platform_name, platforms_dir)
systems = config.get("systems", {})
for sys_id in sorted(systems):
file_count = len(systems[sys_id].get("files", []))
mfr = systems[sys_id].get("manufacturer", "")
mfr_display = f" [{mfr.split('|')[0]}]" if mfr else ""
print(f" {sys_id:35s} ({file_count} file{'s' if file_count != 1 else ''}){mfr_display}")

View File

@@ -27,7 +27,8 @@ sys.path.insert(0, os.path.dirname(__file__))
from common import (
_build_validation_index, build_zip_contents_index, check_file_validation,
check_inside_zip, compute_hashes, fetch_large_file, filter_files_by_mode,
group_identical_platforms, list_emulator_profiles, list_registered_platforms,
group_identical_platforms, list_emulator_profiles, list_platform_system_ids,
list_registered_platforms,
filter_systems_by_target, list_system_ids, load_database,
load_data_dir_registry, load_emulator_profiles, load_platform_config,
md5_composite, resolve_local_file,
@@ -232,6 +233,7 @@ def generate_pack(
emu_profiles: dict | None = None,
target_cores: set[str] | None = None,
required_only: bool = False,
system_filter: list[str] | None = None,
) -> str | None:
"""Generate a ZIP pack for a platform.
@@ -248,7 +250,25 @@ def generate_pack(
version = config.get("version", config.get("dat_version", ""))
version_tag = f"_{version.replace(' ', '')}" if version else ""
req_tag = "_Required" if required_only else ""
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}{req_tag}_BIOS_Pack.zip"
sys_tag = ""
if system_filter:
display_parts = []
for sid in system_filter:
s = sid.lower().replace("_", "-")
for prefix in ("microsoft-", "nintendo-", "sony-", "sega-", "snk-",
"panasonic-", "nec-", "epoch-", "mattel-", "fairchild-",
"hartung-", "tiger-", "magnavox-", "philips-", "bandai-",
"casio-", "coleco-", "commodore-", "sharp-", "sinclair-",
"atari-"):
if s.startswith(prefix):
s = s[len(prefix):]
break
parts = s.split("-")
display_parts.append("_".join(p.title() for p in parts if p))
sys_tag = "_" + "_".join(display_parts)
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}{req_tag}_BIOS_Pack{sys_tag}.zip"
zip_path = os.path.join(output_dir, zip_name)
os.makedirs(output_dir, exist_ok=True)
@@ -281,6 +301,18 @@ def generate_pack(
platform_cores=plat_cores,
)
if system_filter:
from common import _norm_system_id
norm_filter = {_norm_system_id(s) for s in system_filter}
filtered = {sid: sys_data for sid, sys_data in pack_systems.items()
if sid in system_filter or _norm_system_id(sid) in norm_filter}
if not filtered:
available = sorted(pack_systems.keys())[:10]
print(f" WARNING: no systems matched filter {system_filter} "
f"(available: {', '.join(available)})")
return None
pack_systems = filtered
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for sys_id, system in sorted(pack_systems.items()):
for file_entry in system.get("files", []):
@@ -899,7 +931,10 @@ def main():
list_emulator_profiles(args.emulators_dir)
return
if args.list_systems:
list_system_ids(args.emulators_dir)
if args.platform:
list_platform_system_ids(args.platform, args.platforms_dir)
else:
list_system_ids(args.emulators_dir)
return
if args.list_targets:
if not args.platform:
@@ -914,18 +949,26 @@ def main():
print(f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}")
return
# Mutual exclusion
modes = sum(1 for x in (args.platform, args.all, args.emulator, args.system) if x)
if modes == 0:
# Mode validation
has_platform = bool(args.platform)
has_all = args.all
has_emulator = bool(args.emulator)
has_system = bool(args.system)
# --platform/--all and --system can combine (system filters within platform)
# --emulator is exclusive with everything else
if has_emulator and (has_platform or has_all or has_system):
parser.error("--emulator is mutually exclusive with --platform, --all, and --system")
if has_platform and has_all:
parser.error("--platform and --all are mutually exclusive")
if not (has_platform or has_all or has_emulator or has_system):
parser.error("Specify --platform, --all, --emulator, or --system")
if modes > 1:
parser.error("--platform, --all, --emulator, and --system are mutually exclusive")
if args.standalone and not (args.emulator or args.system):
parser.error("--standalone requires --emulator or --system")
if args.target and not (args.platform or args.all):
if args.standalone and not (has_emulator or (has_system and not has_platform and not has_all)):
parser.error("--standalone requires --emulator or --system (without --platform)")
if args.target and not (has_platform or has_all):
parser.error("--target requires --platform or --all")
if args.target and (args.emulator or args.system):
parser.error("--target is incompatible with --emulator and --system")
if args.target and has_emulator:
parser.error("--target is incompatible with --emulator")
db = load_database(args.db)
zip_contents = build_zip_contents_index(db)
@@ -941,8 +984,8 @@ def main():
sys.exit(1)
return
# System mode
if args.system:
# System mode (standalone, without platform context)
if has_system and not has_platform and not has_all:
system_ids = [s.strip() for s in args.system.split(",") if s.strip()]
result = generate_system_pack(
system_ids, args.emulators_dir, db, args.bios_dir, args.output_dir,
@@ -952,6 +995,10 @@ def main():
sys.exit(1)
return
system_filter = None
if args.system:
system_filter = [s.strip() for s in args.system.split(",") if s.strip()]
# Platform mode (existing)
if args.all:
platforms = list_registered_platforms(
@@ -1016,6 +1063,7 @@ def main():
zip_contents=zip_contents, data_registry=data_registry,
emu_profiles=emu_profiles, target_cores=tc,
required_only=args.required_only,
system_filter=system_filter,
)
if zip_path and variants:
rep_cfg = load_platform_config(representative, args.platforms_dir)

View File

@@ -1602,5 +1602,84 @@ class TestE2E(unittest.TestCase):
self.assertTrue(any("present_req.bin" in n for n in names))
def test_132_platform_system_filter(self):
"""--platform + --system filters systems within a platform pack."""
from generate_pack import generate_pack
output_dir = os.path.join(self.root, "pack_sysfilter")
os.makedirs(output_dir, exist_ok=True)
config = {
"platform": "SysFilterTest",
"verification_mode": "existence",
"base_destination": "system",
"systems": {
"system-a": {
"files": [
{"name": "present_req.bin", "destination": "present_req.bin"},
],
},
"system-b": {
"files": [
{"name": "present_opt.bin", "destination": "present_opt.bin"},
],
},
},
}
with open(os.path.join(self.platforms_dir, "test_sysfilter.yml"), "w") as fh:
yaml.dump(config, fh)
zip_path = generate_pack(
"test_sysfilter", self.platforms_dir, self.db, self.bios_dir, output_dir,
system_filter=["system-a"],
)
self.assertIsNotNone(zip_path)
with zipfile.ZipFile(zip_path) as zf:
names = zf.namelist()
self.assertTrue(any("present_req.bin" in n for n in names))
self.assertFalse(any("present_opt.bin" in n for n in names))
def test_133_platform_system_filter_normalized(self):
"""_norm_system_id normalization matches with manufacturer prefix."""
from common import _norm_system_id
self.assertEqual(
_norm_system_id("sony-playstation"),
_norm_system_id("playstation"),
)
def test_134_list_systems_platform_context(self):
"""list_platform_system_ids lists systems from a platform YAML."""
from common import list_platform_system_ids
import io
config = {
"platform": "ListSysTest",
"verification_mode": "existence",
"systems": {
"alpha-sys": {
"files": [
{"name": "a.bin", "destination": "a.bin"},
],
},
"beta-sys": {
"files": [
{"name": "b1.bin", "destination": "b1.bin"},
{"name": "b2.bin", "destination": "b2.bin"},
],
},
},
}
with open(os.path.join(self.platforms_dir, "test_listsys.yml"), "w") as fh:
yaml.dump(config, fh)
captured = io.StringIO()
old_stdout = sys.stdout
sys.stdout = captured
try:
list_platform_system_ids("test_listsys", self.platforms_dir)
finally:
sys.stdout = old_stdout
output = captured.getvalue()
self.assertIn("alpha-sys", output)
self.assertIn("beta-sys", output)
self.assertIn("1 file", output)
self.assertIn("2 files", output)
if __name__ == "__main__":
unittest.main()