From bc8669c45c5e043fd02821cd545925cd0d854f4b Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 22:22:03 +0100 Subject: [PATCH] fix: keep a buildbot core beside its standalone installable_cores removed every buildbot name the image builds, so a libretro core sharing its name with a standalone package was lost: Batocera runs amiberry standalone and its x86_64 boards fetch amiberry_libretro. A name now compares within its build mode, and the resolver and the layouts treat such a core as the emulator's libretro build, run both ways. Batocera gains amiberry, azahar, cannonball, cemu, dosbox, ecwolf, eduke32, fsuae, openmsx, rpcs3, supermodel, vita3k, xemu and ymir as installable, KNULLI cannonball. --- platforms/batocera.yml | 14 ++++++++++++ platforms/knulli.yml | 1 + scripts/common.py | 16 +++++++++++-- scripts/scraper/batocera_scraper.py | 15 ++++++++----- scripts/scraper/knulli_scraper.py | 2 +- tests/test_standalone_links.py | 6 +++-- tests/test_target_scrapers.py | 35 ++++++++++++++++++++++++++++- 7 files changed, 78 insertions(+), 11 deletions(-) diff --git a/platforms/batocera.yml b/platforms/batocera.yml index bde62cb95..99023f988 100644 --- a/platforms/batocera.yml +++ b/platforms/batocera.yml @@ -5803,9 +5803,11 @@ installable_cores: - 3dengine - DoubleCherryGB - amiarcadia +- amiberry - anarch - applewin - ardens +- azahar - b2 - bbkemu - boom3_xp @@ -5818,7 +5820,9 @@ installable_cores: - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance +- cannonball - cdi2015 +- cemu - chailove - citra - citra2018 @@ -5830,10 +5834,13 @@ installable_cores: - dingooemu - dinothawr - dirksimple +- dosbox - dosbox_core - dosbox_svn - dosbox_svn_ce - doukutsu_rs +- ecwolf +- eduke32 - ep128emu_core - fbalpha2012 - fbalpha2012_cps1 @@ -5843,6 +5850,7 @@ installable_cores: - fixgb - fixnes - frodo +- fsuae - galaksija - gam4980 - gametank @@ -5901,6 +5909,7 @@ installable_cores: - odin - omicron - openlara +- openmsx - panda3ds - parallel_dc - pcee2 @@ -5917,6 +5926,7 @@ installable_cores: - remotejoy - retro8 - romcleaner +- rpcs3 - rust_dos - rustynes - sameboy @@ -5928,6 +5938,7 @@ installable_cores: - spmp8000emu - squirreljme - stella2023 +- supermodel - tamalibretro - test - thepowdertoy @@ -5942,13 +5953,16 @@ installable_cores: - vice_xcbm5x0 - video_processor - virtualxt +- vita3k - vitaquake2-rogue - vitaquake2-xatrix - vitaquake2-zaero - vitaquake3 - wqxemu +- xemu - xenia_edge - yabause +- ymir libretro_systems: "81": - zx81 diff --git a/platforms/knulli.yml b/platforms/knulli.yml index dd5b3eff2..8d17ac162 100644 --- a/platforms/knulli.yml +++ b/platforms/knulli.yml @@ -4914,6 +4914,7 @@ installable_cores: - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance +- cannonball - chailove - crocods - dinothawr diff --git a/scripts/common.py b/scripts/common.py index 2dce4b0f6..779067e05 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -2767,6 +2767,15 @@ def standalone_set(config: dict) -> set[str]: """ names = {str(c) for c in config.get("standalone_cores") or []} names |= {DUAL_MODE_PREFIX + str(c) for c in config.get("dual_mode_cores") or []} + # A buildbot core the platform's RetroArch installs under the name of an + # emulator it runs standalone is that emulator's libretro build: run + # both ways, as Batocera runs amiberry standalone and can fetch + # amiberry_libretro. + names |= { + DUAL_MODE_PREFIX + str(c) + for c in set(map(str, config.get("installable_cores") or [])) + & set(map(str, config.get("standalone_cores") or [])) + } # The system directory each core is handed travels the same way, so # platform_layouts places a core's files below it for every reader. names |= { @@ -3264,10 +3273,13 @@ def resolve_platform_cores( # A name the platform launches both ways designates its libretro # build too. Batocera, Recalbox and RetroBat run pcsx2 standalone and # as LRPS2, two profiles: the key match kept the standalone one and - # LRPS2's files never reached their packs. + # LRPS2's files never reached their packs. A core its RetroArch + # installs is a libretro build as well (amiberry's buildbot core + # beside Batocera's standalone amiberry). result |= { profile - for c in config.get("dual_mode_cores") or [] + for c in [*(config.get("dual_mode_cores") or []), + *(config.get("installable_cores") or [])] for profile in index.get(str(c), set()) if "libretro" in str(profiles[profile].get("type", "")) and not profiles[profile].get("built_by") diff --git a/scripts/scraper/batocera_scraper.py b/scripts/scraper/batocera_scraper.py index 36a014dd8..49f797077 100644 --- a/scripts/scraper/batocera_scraper.py +++ b/scripts/scraper/batocera_scraper.py @@ -557,19 +557,24 @@ def boards_build(selection: BuildSelection, launchers: list[dict]) -> Callable[[ return built -def installable_cores(selection: BuildSelection, cores: list[str]) -> list[str]: +def installable_cores( + selection: BuildSelection, cores: list[str], standalone: list[str], dual: list[str], +) -> list[str]: """Buildbot cores a board's RetroArch core downloader installs, beyond - the cores the image builds. + the libretro cores the image builds. The targets scraper names, per board, the RetroArch targets entry its downloader reads (`downloads`): a Batocera x86_64 board installs any - core of linux-x86_64, an armv7 one any of linux-armhf. + core of linux-x86_64, an armv7 one any of linux-armhf. A name compares + within its build mode: amiberry built standalone is another program + than amiberry_libretro, which the buildbot serves. """ found: set[str] = set() for entry in selection.targets.values(): if entry.get("downloads"): found |= load_target_config("retroarch", entry["downloads"]) or set() - return sorted(found - set(cores)) + built_libretro = (set(cores) - set(standalone)) | set(dual) + return sorted(found - built_libretro) def records_run(launchers: list[dict], built: Callable[[str], bool]) -> list[dict]: @@ -642,7 +647,7 @@ class Scraper(BaseScraper): built = boards_build(selection, launchers) launchers = records_run(launchers, built) cores, standalone, dual = self._fetch_cores(built) - installable = installable_cores(selection, cores) + installable = installable_cores(selection, cores, standalone, dual) libretro_systems = es_systems_libretro_systems( self._fetch_text(ES_SYSTEMS_URL.format(tag=self.tag)), _not_cores() ) diff --git a/scripts/scraper/knulli_scraper.py b/scripts/scraper/knulli_scraper.py index 0522ae53a..6ec04da61 100644 --- a/scripts/scraper/knulli_scraper.py +++ b/scripts/scraper/knulli_scraper.py @@ -141,7 +141,7 @@ class Scraper(BaseScraper): built = boards_build(selection, launchers) launchers = records_run(launchers, built) cores, standalone, dual = configgen_cores(defaults, es_systems, dropped, built) - installable = installable_cores(selection, cores) + installable = installable_cores(selection, cores, standalone, dual) # Each system's own list (psx, es_systems.yml:640-653: libretro # pcsx_rearmed, swanstation, mednafen_psx, then duckstation and # duckstation-legacy). diff --git a/tests/test_standalone_links.py b/tests/test_standalone_links.py index 745a4addc..d2c3df8f3 100644 --- a/tests/test_standalone_links.py +++ b/tests/test_standalone_links.py @@ -660,13 +660,15 @@ class AmiberryOnEachPlatform(unittest.TestCase): class AmiberryBuildEachPlatformShips(unittest.TestCase): """Each platform resolves the name amiberry to the build its own package - compiles. RetroBat keeps the 8.5.0 libretro core beside its standalone.""" + compiles. RetroBat keeps the 8.5.0 libretro core beside its standalone, + and Batocera's x86_64 boards fetch it with RetroArch's core downloader + (installable_cores).""" OWN = { "rocknix": {"amiberry-5.6.5"}, "amberelec": {"amiberry-7.1.1"}, "emuelec": {"amiberry-8.0.0"}, - "batocera": {"amiberry-7.1.1-332"}, + "batocera": {"amiberry", "amiberry-7.1.1-332"}, "knulli": {"amiberry-7.1.1"}, "recalbox": {"amiberry-8.2.2"}, "retrobat": {"amiberry", "amiberry-8.3.0"}, diff --git a/tests/test_target_scrapers.py b/tests/test_target_scrapers.py index 8c17df288..65a90da7d 100644 --- a/tests/test_target_scrapers.py +++ b/tests/test_target_scrapers.py @@ -299,7 +299,40 @@ class CoreDownloaderReach(unittest.TestCase): if entry["architecture"] == "aarch64": self.assertNotIn("downloads", entry) with self.subTest(platform=platform): - self.assertEqual(set(config["installable_cores"]), fetched - set(config["cores"])) + built = ((set(config["cores"]) - set(config.get("standalone_cores") or [])) + | set(config.get("dual_mode_cores") or [])) + self.assertEqual(set(config["installable_cores"]), fetched - built) + + def test_a_standalone_name_keeps_its_buildbot_core(self): + """Batocera runs amiberry standalone, and its x86_64 boards fetch + amiberry_libretro: removing every name the image builds lost the + libretro core to the name it shares with the standalone package.""" + from common import DUAL_MODE_PREFIX, resolve_platform_cores, standalone_set # noqa: PLC0415 + from scraper.batocera_scraper import installable_cores # noqa: PLC0415 + from scraper.targets.batocera_targets_scraper import BuildSelection # noqa: PLC0415 + + selection = BuildSelection( + targets={"x86_64": {"architecture": "x86_64", "cores": [], + "downloads": "linux-x86_64"}}, + emulators=frozenset(), listed=frozenset(), symbols=frozenset(), packages={}, + ) + found = installable_cores(selection, ["amiberry", "mgba", "dolphin"], + ["amiberry", "dolphin"], ["dolphin"]) + self.assertIn("amiberry", found) + self.assertNotIn("mgba", found) + self.assertNotIn("dolphin", found) + + profiles = { + "amiberry": {"type": "libretro", "cores": ["amiberry"], "systems": ["amiga"]}, + "amiberry-7": {"type": "standalone", "cores": ["amiberry"], "systems": ["amiga"], + "built_by": ["demo"], "source": "https://x/amiberry"}, + } + config = {"_key": "demo", "cores": ["amiberry"], "standalone_cores": ["amiberry"], + "installable_cores": ["amiberry"]} + self.assertEqual(resolve_platform_cores(config, profiles), {"amiberry", "amiberry-7"}) + self.assertEqual(resolve_platform_cores({**config, "installable_cores": []}, profiles), + {"amiberry-7"}) + self.assertIn(DUAL_MODE_PREFIX + "amiberry", standalone_set(config)) class KnulliPerBoard(unittest.TestCase):