fix: let a platform's only target filter nothing

This commit is contained in:
Abdessamad Derraz committed 2026-10-17 14:48:35 +02:00
1 parent 8d39b1a270
commit 426d3c619d
12 files changed
+105 -266

No files matched your search

+1 -154
View File
@@ -1,156 +1,3 @@
{
"windows": [
"81",
"a5200",
"abuse",
"arduous",
"atari800",
"azahar",
"bennugd",
"bk",
"bluemsx",
"bsnes",
"bstone",
"cannonball",
"cap32",
"catacombgl",
"cdogs",
"cemu",
"cgenius",
"citron",
"clk",
"corsixth",
"demul",
"devilutionx",
"dhewm3",
"dice",
"dolphin",
"dosbox_pure",
"dxx-rebirth",
"easyrpg",
"ecwolf",
"eduke32",
"eka2l1",
"emuscv",
"etlegacy",
"fake08",
"fallout1-ce",
"fallout2-ce",
"fbneo",
"fceumm",
"flycast",
"freechaf",
"freeintv",
"fury",
"fuse",
"gambatte",
"gearsystem",
"genesisplusgx",
"gong",
"gsplus",
"gw",
"gzdoom",
"hatari",
"hcl",
"hurrican",
"hypseus-singe",
"ikemen",
"ioquake3",
"iortcw",
"jazz2-native",
"lindbergh-loader",
"lowresnx",
"lutro",
"mame",
"mame078plus",
"mednafen_lynx",
"mednafen_ngp",
"mednafen_supergrafx",
"mednafen_wswan",
"melonds",
"mgba",
"minivmac",
"model2emu",
"moonlight",
"mrboom",
"neocd",
"np2kai",
"nxengine",
"o2em",
"openbor6412",
"openjazz",
"openjk",
"openjkdf2",
"openmohaa",
"opera",
"pce_fast",
"pcfx",
"pcsx2",
"pcsx_rearmed",
"pd777",
"picodrive",
"play",
"pokemini",
"potator",
"ppsspp",
"prboom",
"prosystem",
"puae",
"px68k",
"pygame",
"pyxel",
"quasi88",
"raze",
"reminiscence",
"rpcs3",
"ruffle",
"samcoupe",
"sameduck",
"scummvm",
"sdlpop",
"shadps4",
"snes9x",
"solarus",
"sonic-mania",
"sonic2013",
"sonic3-air",
"stella",
"superbroswar",
"supermodel",
"taradino",
"tgbdual",
"theforceengine",
"theodore",
"thextech",
"tic80",
"tr1x",
"tr2x",
"tsugaru",
"tyrian",
"tyrquake",
"uqm",
"uzem",
"vb",
"vecx",
"vice_x64",
"vircon32",
"virtualjaguar",
"vita3k",
"vox_official",
"vpinball",
"wasm4",
"x1",
"x128",
"x16emu",
"xash3d_fwgs",
"xemu",
"xenia-canary",
"xpet",
"xplus4",
"xrick",
"xvic",
"yabasanshiro",
"yquake2",
"zc210"
]
"windows": null
}
+1 -20
View File
@@ -1,22 +1,3 @@
{
"x86_64-linux": [
"azahar",
"cemu",
"dolphin",
"duckstation",
"gzdoom",
"mame",
"melonds",
"openbor",
"pcsx2",
"pico-8",
"ppsspp",
"primehack",
"rpcs3",
"ruffle",
"solarus",
"vita3k",
"xemu",
"xroar"
]
"x86_64-linux": null
}
+1 -49
View File
@@ -1,51 +1,3 @@
{
"browser": [
"a5200",
"beetle_ngp",
"beetle_pcfx",
"beetle_psx",
"beetle_vb",
"cap32",
"crocods",
"desmume",
"desmume2015",
"dosbox_pure",
"fbalpha2012_cps1",
"fbalpha2012_cps2",
"fbneo",
"fceumm",
"freeintv",
"fuse",
"gambatte",
"gearcoleco",
"genesis_plus_gx",
"handy",
"mame2003",
"mame2003_plus",
"mednafen_pce",
"melonds",
"mgba",
"mupen64plus_next",
"nestopia",
"opera",
"parallel_n64",
"pcsx_rearmed",
"picodrive",
"ppsspp",
"prboom",
"prosystem",
"puae",
"same_cdi",
"smsplus",
"snes9x",
"stella2014",
"vice_x128",
"vice_x64",
"vice_x64sc",
"vice_xpet",
"vice_xplus4",
"vice_xvic",
"virtualjaguar",
"yabause"
]
"browser": null
}
+4 -20
View File
@@ -1,25 +1,9 @@
platform: retrobat
source: static
scraped_at: "2026-03-26T00:00:00Z"
targets:
# RetroBat runs on Windows alone: every core the platform declares runs on this target, and
# naming it filters nothing. A hand-kept list here fell behind the scraped
# platform and dropped cores the pack ships.
windows:
architecture: x86_64
cores: ['81', a5200, abuse, arduous, atari800, azahar, bennugd, bk, bluemsx, bsnes,
bstone, cannonball, cap32, catacombgl, cdogs, cemu, cgenius, citron, clk, corsixth,
demul, devilutionx, dhewm3, dice, dolphin, dosbox_pure, dxx-rebirth, easyrpg,
ecwolf, eduke32, eka2l1, emuscv, etlegacy, fake08, fallout1-ce, fallout2-ce,
fbneo, fceumm, flatpak, flycast, freechaf, freeintv, fury, fuse, gambatte,
gearsystem, genesisplusgx, glide64mk2, gong, gsplus, gw, gzdoom, hatari, hcl,
hurrican, hypseus-singe, ikemen, ioquake3, iortcw, jazz2-native, lindbergh-loader,
lowresnx, lutro, mame, mame078plus, mednafen_lynx, mednafen_ngp, mednafen_supergrafx,
mednafen_wswan, melonds, mgba, minivmac, model2emu, moonlight, mrboom, neocd,
np2kai, nxengine, o2em, odcommander, openbor6412, openjazz, openjk, openjkdf2,
openmohaa, opera, pce_fast, pcfx, pcsx2, pcsx_rearmed, pd777, picodrive, play,
pokemini, potator, ppsspp, prboom, prosystem, puae, px68k, pygame, pyxel, quasi88,
raze, reminiscence, rpcs3, ruffle, samcoupe, sameduck, scummvm, sdlpop, sh,
shadps4, snes9x, solarus, sonic2013, sonic3-air, sonic-mania, steam, stella,
superbroswar, supermodel, taradino, tgbdual, theforceengine, theodore, thextech,
tic80, tr1x, tr2x, tsugaru, tyrian, tyrquake, uqm, uzem, vb, vecx, vice_x64,
vircon32, virtualjaguar, vita3k, vox_official, vpinball, wasm4, wine-tkg, x1,
x128, x16emu, xash3d_fwgs, xemu, xenia-canary, xpet, xplus4, xrick, xvic,
yabasanshiro, yquake2, zc210]
cores: null
+4 -4
View File
@@ -1,9 +1,9 @@
platform: retrodeck
source: static
scraped_at: "2026-03-26T00:00:00Z"
targets:
# RetroDECK runs on x86_64 Linux alone: every core the platform declares runs on this target, and
# naming it filters nothing. A hand-kept list here fell behind the scraped
# platform and dropped cores the pack ships.
x86_64-linux:
architecture: x86_64
cores: [azahar, cemu, dolphin, duckstation, gzdoom, mame, melonds, openbor, pcsx2,
pico-8, ppsspp, primehack, retroarch, rpcs3, ruffle, solarus, vita3k, xemu,
xroar]
cores: null
+4 -2
View File
@@ -1,7 +1,9 @@
platform: romm
source: static
scraped_at: "2026-03-26T00:00:00Z"
targets:
# RomM plays in the browser alone, through EmulatorJS: every core the platform declares runs on this target, and
# naming it filters nothing. A hand-kept list here fell behind the scraped
# platform and dropped cores the pack ships.
browser:
architecture: wasm
cores: [a5200, beetle_ngp, beetle_pcfx, beetle_psx, beetle_vb, cap32, crocods, desmume, desmume2015, dosbox_pure, fbalpha2012_cps1, fbalpha2012_cps2, fbneo, fceumm, freeintv, fuse, gambatte, gearcoleco, genesis_plus_gx, handy, mame2003, mame2003_plus, mednafen_pce, melonds, mgba, mupen64plus_next, nestopia, opera, parallel_n64, pcsx_rearmed, picodrive, ppsspp, prboom, prosystem, puae, same_cdi, smsplus, snes9x, stella2014, vice_x128, vice_x64, vice_x64sc, vice_xpet, vice_xplus4, vice_xvic, virtualjaguar, yabause]
cores: null
+8
View File
@@ -436,6 +436,14 @@ def check_targets(
with path.open(encoding="utf-8") as fh:
old = yaml_load(fh) or {}
module = scrapers.get(platform)
targets = old.get("targets") or {}
if module is None and targets and all(
(t or {}).get("cores", []) is None for t in targets.values()
):
# Every core the platform declares runs on these targets: the list
# is the platform's own and has nothing of its own to age.
return [Finding("targets", platform, OK, local="static",
detail="every declared core")]
if module is None:
# No scraper: a hand-written file has no upstream to diff against,
# so only its age and its core names can be reported.
+24 -5
View File
@@ -437,10 +437,14 @@ def load_target_config(
platform_name: str,
target: str,
platforms_dir: str = "platforms",
) -> set[str]:
) -> set[str] | None:
"""Load target config and return the set of core names for the given target.
Resolves aliases from _overrides.yml, applies add_cores/remove_cores.
A target whose cores are null runs every core the platform declares and
returns None, which filters nothing: the platforms with a single target
(RetroBat on Windows, RetroDECK, RomM in the browser) kept a hand list
that dropped 98 RetroBat cores and 300 RetroDECK ones.
Raises ValueError if target is unknown (with list of available targets).
Raises FileNotFoundError if no target file exists for the platform.
"""
@@ -470,7 +474,10 @@ def load_target_config(
msg += f"\nAliases: {', '.join(sorted(aliases))}"
raise ValueError(msg)
cores = set(str(c) for c in targets[canonical].get("cores", []))
declared = (targets[canonical] or {}).get("cores")
if declared is None:
return None
cores = set(str(c) for c in declared)
return apply_target_overrides(cores, overrides, canonical)
@@ -515,18 +522,30 @@ def list_available_targets(
result = []
for tname, tdata in sorted(data.get("targets", {}).items()):
aliases = (overrides.get(tname) or {}).get("aliases", [])
cores = {str(c) for c in tdata.get("cores", [])}
declared = (tdata or {}).get("cores")
# None: the target runs every core the platform declares.
count = (
None if declared is None
else len(apply_target_overrides({str(c) for c in declared}, overrides, tname))
)
result.append(
{
"name": tname,
"architecture": tdata.get("architecture", ""),
"core_count": len(apply_target_overrides(cores, overrides, tname)),
"architecture": (tdata or {}).get("architecture", ""),
"core_count": count,
"aliases": aliases,
}
)
return result
def target_listing_line(row: dict) -> str:
"""One --list-targets row, as verify and generate_pack print it."""
aliases = f" (aliases: {', '.join(row['aliases'])})" if row["aliases"] else ""
count = "all" if row["core_count"] is None else str(row["core_count"])
return f" {row['name']:30s} {row['architecture']:10s} {count:>4s} cores{aliases}"
HASH_EXACT_RESOLUTION_STATUSES = frozenset(
{
"sha1_exact",
+2 -4
View File
@@ -30,6 +30,7 @@ from pathlib import Path
sys.path.insert(0, os.path.dirname(__file__))
from common import resolve_platform_cores as _platform_cores
from common import (
target_listing_line,
suggest_names,
require_platform,
apply_target_overrides,
@@ -2897,10 +2898,7 @@ def main():
print(f"No targets configured for platform '{args.platform}'")
return
for t in targets:
aliases = f" (aliases: {', '.join(t['aliases'])})" if t["aliases"] else ""
print(
f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}"
)
print(target_listing_line(t))
return
_validate_args(args, parser)
+2 -4
View File
@@ -34,6 +34,7 @@ import slots
sys.path.insert(0, os.path.dirname(__file__))
from common import (
target_listing_line,
suggest_names,
require_platform,
archive_entry,
@@ -2029,10 +2030,7 @@ def _run_listing(args: argparse.Namespace, parser: argparse.ArgumentParser) -> N
print(f"No targets configured for platform '{args.platform}'")
return
for t in targets:
aliases = f" (aliases: {', '.join(t['aliases'])})" if t["aliases"] else ""
print(
f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}"
)
print(target_listing_line(t))
def main():
+15
View File
@@ -303,6 +303,21 @@ class RepositoryWiringTests(unittest.TestCase):
self.assertIn("[ci]", text)
self.assertIn("[data]", text)
def test_a_target_of_every_declared_core_does_not_age(self):
"""RetroBat, RetroDECK and RomM name one target that runs every core
they declare; with nothing hand-kept, there is no date to watch."""
import tempfile # noqa: PLC0415
from pathlib import Path as _Path # noqa: PLC0415
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp:
targets = _Path(tmp) / "targets"
targets.mkdir()
(targets / "solo.yml").write_text(
"platform: solo\nsource: static\ntargets:\n only:\n cores: null\n")
findings = cf.check_targets(_Path(tmp), _Path(tmp), {}, 1, True)
solo = [f for f in findings if f.subject == "solo"]
self.assertEqual([(f.status, f.detail) for f in solo], [(cf.OK, "every declared core")])
def test_render_keeps_long_values_whole(self):
"""The profile_sync row lost the end of its sentence at 32 characters."""
local = "12 profiles behind upstream, 103 refs to re-read"
+39 -4
View File
@@ -90,12 +90,47 @@ class TheListingCountsWhatTheFilterUses(unittest.TestCase):
platform = target_file.stem
for row in list_available_targets(platform, platforms):
with self.subTest(platform=platform, target=row["name"]):
self.assertEqual(
row["core_count"],
len(load_target_config(platform, row["name"], platforms)),
)
resolved = load_target_config(platform, row["name"], platforms)
self.assertEqual(row["core_count"],
None if resolved is None else len(resolved))
checked += 1
self.assertGreater(checked, 0)
class ASingleTargetFiltersNothing(unittest.TestCase):
"""CLAUDE.md's rule, held on the real data: RetroBat, RetroDECK and RomM
have one target each, and hand-kept lists dropped 98, 300 and 132 cores
from a pack the user had only asked to name."""
def test_the_only_target_resolves_every_declared_core(self):
import glob # noqa: PLC0415
import yaml # noqa: PLC0415
from common import ( # noqa: PLC0415
load_emulator_profiles,
load_platform_config,
load_target_config,
resolve_platform_cores,
)
platforms = str(REPO_ROOT / "platforms")
profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
checked = 0
for path in sorted(glob.glob(str(REPO_ROOT / "platforms" / "targets" / "*.yml"))):
name = Path(path).stem
if name.startswith("_"):
continue
targets = yaml.safe_load(Path(path).read_text())["targets"]
if len(targets) != 1:
continue
with self.subTest(platform=name):
config = load_platform_config(name, platforms)
only = load_target_config(name, next(iter(targets)), platforms)
self.assertEqual(resolve_platform_cores(config, profiles, target_cores=only),
resolve_platform_cores(config, profiles))
checked += 1
self.assertGreaterEqual(checked, 3)
if __name__ == "__main__":
unittest.main()