mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 10:03:49 -06:00
feat: add EmuELEC hardware targets
The v4.8 release publishes four images (Amlogic-ng, GameForce, OdroidGoAdvance, OdroidM1, read from its asset names). The metapackage (packages/sx05re/emuelec/package.mk:8-77) builds the S922X cores, dolphinSA and mame only on Amlogic-ng, Amlogic-no, RK356x and OdroidM1, and removes duckstation on the handhelds with a sed that replaces the first match only: pcsx_rearmed, written twice, keeps its 64-bit build. Each package is mapped to its cores by the .so or program its install step copies, PKG_ARCH is applied as scripts/build:28-30 does, and es_systems.cfg is edited per device as emuelec-emulationstation/package.mk:114-143 does. Amlogic-ng and OdroidM1 list 124 cores, the handhelds 117.
This commit is contained in:
1 parent
2e98be9366
commit
5e90a455e4
8 files changed
+1435
-6
No files matched your search
@@ -0,0 +1,662 @@
|
||||
"""Scraper for EmuELEC per-device emulator availability.
|
||||
|
||||
Sources (EmuELEC/EmuELEC, at the tag platforms/emuelec.yml names, v4.8):
|
||||
- the release's asset names: one `EmuELEC-<DEVICE>.<ARCH>-<version>`
|
||||
image per device (Amlogic-ng, GameForce, OdroidGoAdvance, OdroidM1 at
|
||||
v4.8). The tree names no device list: projects/ holds the directories
|
||||
of many more devices than the release builds.
|
||||
- packages/sx05re/emuelec/package.mk -- the metapackage, whose
|
||||
PKG_DEPENDS_TARGET is each device's package list (lines 8-77)
|
||||
- distributions/EmuELEC/options -- LIBRETRO_CORES and
|
||||
LIBRETRO_S922X_CORES (lines 274-373)
|
||||
- each core package's package.mk -- PKG_ARCH, which scripts/build:28-30
|
||||
reads to skip a package, and the install step naming the .so or binary
|
||||
- packages/sx05re/emuelec-emulationstation/package.mk -- the cores and
|
||||
emulators makeinstall_target deletes from es_systems.cfg per device
|
||||
(lines 114-143)
|
||||
- es_systems.json and ports.yaml, read the way the platform scraper reads
|
||||
them
|
||||
|
||||
A device keeps a core when a package it builds installs that core and its
|
||||
es_systems.cfg still offers it. The OdroidGoAdvance image carries neither
|
||||
DuckStation nor Dolphin nor the MAME core: without a target list, its pack
|
||||
shipped their firmware anyway.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from typing import NamedTuple
|
||||
|
||||
import yaml
|
||||
|
||||
from . import BaseTargetScraper
|
||||
|
||||
PLATFORM_NAME = "emuelec"
|
||||
|
||||
REPO = "EmuELEC/EmuELEC"
|
||||
RAW = "https://raw.githubusercontent.com/EmuELEC/EmuELEC/{ref}/{path}"
|
||||
RELEASE_API = "https://api.github.com/repos/EmuELEC/EmuELEC/releases/tags/{ref}"
|
||||
META_MK = "packages/sx05re/emuelec/package.mk"
|
||||
OPTIONS = "distributions/EmuELEC/options"
|
||||
ES_MK = "packages/sx05re/emuelec-emulationstation/package.mk"
|
||||
|
||||
try:
|
||||
from ..base_scraper import fetch_bytes, github_headers
|
||||
from ..batocera_scraper import _not_cores
|
||||
from ..emuelec_scraper import (
|
||||
BUILDS_OF, ES_SYSTEMS_TEMPLATE, PORTS_TEMPLATE, RUNNERS, port_engines, with_ports,
|
||||
)
|
||||
from ..es_emulators import SystemEmulators, es_cores
|
||||
except ImportError:
|
||||
from scraper.base_scraper import fetch_bytes, github_headers
|
||||
from scraper.batocera_scraper import _not_cores
|
||||
from scraper.emuelec_scraper import (
|
||||
BUILDS_OF, ES_SYSTEMS_TEMPLATE, PORTS_TEMPLATE, RUNNERS, port_engines, with_ports,
|
||||
)
|
||||
from scraper.es_emulators import SystemEmulators, es_cores
|
||||
|
||||
_ASSET_RE = re.compile(r"^EmuELEC-(?P<device>[A-Za-z0-9-]+)\.(?P<arch>[a-z0-9_]+)-")
|
||||
|
||||
# The metapackage, line for line. Each rule below transcribes these lines;
|
||||
# a tag that rewrites one stops the scrape instead of reading it wrong.
|
||||
_META_LINES = (
|
||||
'PKG_DEPENDS_TARGET="toolchain ${OPENGLES} emuelec-emulationstation retroarch"',
|
||||
'PKG_DEPENDS_TARGET+=" emuelec-tools ${PKG_EMUS} ${PKG_EXPERIMENTAL}"',
|
||||
'if [ "${DEVICE}" == "Amlogic-ng" ] || [ "${DEVICE}" == "Amlogic-no" ] || '
|
||||
'[ "${DEVICE}" == "RK356x" ] || [ "${DEVICE}" == "OdroidM1" ]; then',
|
||||
'PKG_DEPENDS_TARGET+=" ${LIBRETRO_S922X_CORES}"',
|
||||
'if [ "${DEVICE}" == "OdroidGoAdvance" ] || [ "${DEVICE}" == "GameForce" ]; then',
|
||||
'PKG_DEPENDS_TARGET+=" kmscon odroidgoa-utils"',
|
||||
"for discore in duckstation mesen-s virtualjaguar quicknes MC; do",
|
||||
'PKG_DEPENDS_TARGET=$(echo ${PKG_DEPENDS_TARGET} | sed "s|${discore} | |")',
|
||||
'PKG_DEPENDS_TARGET+=" yabasanshiro"',
|
||||
'PKG_DEPENDS_TARGET+=" fbterm"',
|
||||
'if [ "${ARCH}" == "aarch64" ]; then',
|
||||
"for discore in quicknes parallel-n64 pcsx_rearmed; do",
|
||||
'PKG_DEPENDS_TARGET=$(echo ${PKG_DEPENDS_TARGET} | sed "s|${discore}| |")',
|
||||
'PKG_DEPENDS_TARGET+=" dolphinSA"',
|
||||
'if [ "${DEVICE}" == "Amlogic-old" ]; then',
|
||||
"for discore in yabasanshiroSA_1_11 yabasanshiroSA_1_5 yabasanshiro same_cdi duckstation; do",
|
||||
'PKG_DEPENDS_TARGET+=" mame"',
|
||||
'if [ "${DEVICE}" == "RK356x" ] || [ "${DEVICE}" == "OdroidM1" ]; then',
|
||||
"for discore in flycast-dojo; do",
|
||||
)
|
||||
_LIB32_RE = re.compile(r'PKG_DEPENDS_TARGET\+=" (swanstation [^"]*)"')
|
||||
_BIG = ("Amlogic-ng", "Amlogic-no", "RK356x", "OdroidM1")
|
||||
_HANDHELD = ("OdroidGoAdvance", "GameForce")
|
||||
|
||||
# What emuelec-emulationstation's makeinstall_target does to es_systems.cfg.
|
||||
_ES_LINES = (
|
||||
'if [[ "${DEVICE}" != "Amlogic-ng" || "${DEVICE}" != "Amlogic-ne" || '
|
||||
'"${DEVICE}" != "Amlogic-no" ]]; then',
|
||||
'if [[ ${DEVICE} == "OdroidGoAdvance" || "${DEVICE}" == "GameForce" ]]; then',
|
||||
'remove_cores="mesen-s quicknes mame2016 mesen"',
|
||||
'remove_cores="mesen-s quicknes mame2016 mesen yabasanshiroSA yabasanshiro"',
|
||||
"xmlstarlet ed -L -P -d \"/systemList/system[name='saturn']\" ${CORESFILE}",
|
||||
"xmlstarlet ed -L -P -d \"/systemList/system[name='phillips-cdi']\" ${CORESFILE}",
|
||||
'sed -i "s|<core>${discore}</core>||g" ${CORESFILE}',
|
||||
'if [[ ${DEVICE} != "OdroidGoAdvance" ]] && [[ "${DEVICE}" != "GameForce" ]]; then',
|
||||
"xmlstarlet ed -L -P -d \"/systemList/system/emulators/emulator[@name='retrorun']\" ${CORESFILE}",
|
||||
"xmlstarlet ed -L -P -d \"/systemList/system/emulators/emulator[@name='Duckstation']\" ${CORESFILE}",
|
||||
)
|
||||
|
||||
|
||||
class Install(NamedTuple):
|
||||
"""One line of a package.mk that puts a core on the image."""
|
||||
|
||||
path: str
|
||||
line: str
|
||||
cores: tuple[str, ...]
|
||||
# The ARCH test the line sits under in makeinstall_target, if any.
|
||||
arch: str | None = None
|
||||
|
||||
|
||||
def _lr(package: str, line: str, *cores: str, arch: str | None = None) -> Install:
|
||||
return Install(f"packages/sx05re/libretro/{package}/package.mk", line, cores, arch)
|
||||
|
||||
|
||||
def _sa(package: str, line: str, *cores: str) -> Install:
|
||||
return Install(f"packages/sx05re/emulators/{package}/package.mk", line, cores)
|
||||
|
||||
|
||||
def _lib32(package: str, line: str, *cores: str) -> Install:
|
||||
return Install(f"packages/lib32/emuelec/libretro/{package}/package.mk", line, cores)
|
||||
|
||||
|
||||
_TO = "${INSTALL}/usr/lib/libretro/"
|
||||
|
||||
# Package -> the ES core names its install step puts on the image, with the
|
||||
# line that does it (v4.8). A libretro core is the .so it copies to
|
||||
# /usr/lib/libretro, the name emuelecRunEmu.sh:101-116 loads as
|
||||
# ${CORE}_libretro. A standalone name is the program the runner's case for
|
||||
# it starts (emuelecRunEmu.sh:164-353), installed by the line cited, or by
|
||||
# the package's default make/cmake/meson install where the line cited only
|
||||
# adds the launch script beside it.
|
||||
PROVIDERS: dict[str, tuple[Install, ...]] = {
|
||||
"2048": (_lr("2048", f"cp 2048_libretro.so {_TO}", "2048"),),
|
||||
"81": (_lr("81", f"cp 81_libretro.so {_TO}", "81"),),
|
||||
"a5200": (_lr("a5200", f"cp a5200_libretro.so {_TO}", "a5200"),),
|
||||
"atari800": (_lr("atari800", f"cp atari800_libretro.so {_TO}", "atari800"),),
|
||||
"beetle-lynx": (_lr("beetle-lynx", f"cp mednafen_lynx_libretro.so {_TO}", "mednafen_lynx"),),
|
||||
"beetle-ngp": (_lr("beetle-ngp", f"cp mednafen_ngp_libretro.so {_TO}", "mednafen_ngp"),),
|
||||
"beetle-pce": (_lr("beetle-pce", f"cp mednafen_pce_fast_libretro.so {_TO}",
|
||||
"mednafen_pce_fast"),),
|
||||
"beetle-pcfx": (_lr("beetle-pcfx", f"cp mednafen_pcfx_libretro.so {_TO}", "mednafen_pcfx"),),
|
||||
# line 20: the build makes mednafen_saturn_libretro.so (Makefile TARGET_NAME).
|
||||
"beetle-saturn": (_lr("beetle-saturn", "cp mednafen_saturn_*.so $INSTALL/usr/lib/libretro/",
|
||||
"mednafen_saturn"),),
|
||||
"beetle-supergrafx": (_lr("beetle-supergrafx", f"cp mednafen_supergrafx_libretro.so {_TO}",
|
||||
"mednafen_supergrafx"),),
|
||||
"beetle-vb": (_lr("beetle-vb", f"cp mednafen_vb_libretro.so {_TO}", "mednafen_vb"),),
|
||||
"beetle-wswan": (_lr("beetle-wswan", f"cp mednafen_wswan_libretro.so {_TO}",
|
||||
"mednafen_wswan"),),
|
||||
"bk": (_lr("bk", f"cp ${{PKG_BUILD}}/bk_libretro.so {_TO}", "bk"),),
|
||||
"bluemsx": (_lr("bluemsx", f"cp bluemsx_libretro.so {_TO}", "bluemsx"),),
|
||||
"cannonball": (_lr("cannonball", f"cp cannonball_libretro.so {_TO}", "cannonball"),),
|
||||
"cap32": (_lr("cap32", f"cp cap32_libretro.so {_TO}", "cap32"),),
|
||||
"daphne": (_lr("daphne", f"cp daphne_libretro.so {_TO}", "daphne"),),
|
||||
"dinothawr": (_lr("dinothawr", f"cp dinothawr_libretro.so {_TO}", "dinothawr"),),
|
||||
"dosbox-pure": (_lr("dosbox-pure", f"cp dosbox_pure_libretro.so {_TO}dosbox_pure_libretro.so",
|
||||
"dosbox_pure"),),
|
||||
"dosbox-svn": (_lr("dosbox-svn", "cp ${PKG_BUILD}/libretro/dosbox_svn_libretro.so "
|
||||
"${INSTALL}/usr/lib/libretro", "dosbox_svn"),),
|
||||
"easyrpg": (_lr("easyrpg", f"cp ${{PKG_BUILD}}/.${{TARGET_NAME}}/easyrpg_libretro.so {_TO}",
|
||||
"easyrpg"),),
|
||||
"fake_08": (_lr("fake_08", f"cp -v ${{PKG_BUILD}}/platform/libretro/fake08_libretro.so {_TO}",
|
||||
"fake08"),),
|
||||
"fbalpha2012": (_lr("fbalpha2012", f"cp fbalpha2012_libretro.so {_TO}", "fbalpha2012"),),
|
||||
"fbneo": (_lr("fbneo", f"cp ${{PKG_BUILD}}/src/burner/libretro/fbneo_libretro.so {_TO}",
|
||||
"fbneo"),),
|
||||
"fceumm": (_lr("fceumm", f"cp fceumm_libretro.so {_TO}", "fceumm"),),
|
||||
"fceumm-mod": (_lr("fceumm-mod", f"cp ${{PKG_BUILD}}/fceumm_mod_libretro.* {_TO}",
|
||||
"fceumm_mod"),),
|
||||
# lines 47-51: flycast_32b on arm, flycast elsewhere.
|
||||
"flycast": (
|
||||
_lr("flycast", f"cp flycast_libretro.so {_TO}flycast_32b_libretro.so", "flycast_32b",
|
||||
arch="arm"),
|
||||
_lr("flycast", f"cp flycast_libretro.so {_TO}", "flycast", arch="!arm"),
|
||||
),
|
||||
"fmsx-libretro": (_lr("fmsx-libretro", f"cp fmsx_libretro.so {_TO}", "fmsx"),),
|
||||
"freeintv": (_lr("freeintv", f"cp freeintv_libretro.so {_TO}", "freeintv"),),
|
||||
"freej2me": (_lr("freej2me", 'cp `find . -name "freej2me_libretro.so" | xargs echo` '
|
||||
"${INSTALL}/usr/lib/libretro", "freej2me"),),
|
||||
"fuse-libretro": (_lr("fuse-libretro", f"cp fuse_libretro.so {_TO}", "fuse"),),
|
||||
"gambatte": (_lr("gambatte", f"cp gambatte_libretro.so {_TO}", "gambatte"),),
|
||||
"gearboy": (_lr("gearboy", f"cp platforms/libretro/gearboy_libretro.so {_TO}", "gearboy"),),
|
||||
"gearcoleco": (_lr("gearcoleco", f"cp platforms/libretro/gearcoleco_libretro.so {_TO}",
|
||||
"gearcoleco"),),
|
||||
"geargrafx": (_lr("geargrafx", f"cp platforms/libretro/geargrafx_libretro.so {_TO}",
|
||||
"geargrafx"),),
|
||||
"gearlynx": (_lr("gearlynx", f"cp platforms/libretro/gearlynx_libretro.so {_TO}",
|
||||
"gearlynx"),),
|
||||
"gearsystem": (_lr("gearsystem", f"cp platforms/libretro/gearsystem_libretro.so {_TO}",
|
||||
"gearsystem"),),
|
||||
"genesis-plus-gx": (_lr("genesis-plus-gx",
|
||||
f"cp genesis_plus_gx_libretro.so {_TO}genesis_plus_gx_libretro.so",
|
||||
"genesis_plus_gx"),),
|
||||
"genesis-plus-gx-wide": (_lr("genesis-plus-gx-wide", "cp genesis_plus_gx_wide_libretro.so "
|
||||
f"{_TO}genesis_plus_gx_wide_libretro.so",
|
||||
"genesis_plus_gx_wide"),),
|
||||
# PKG_NAME is genesis_plus_gx_cart_special (line 4).
|
||||
"genesis_plus_gx_cart_special": (_lr("genesis_plus_gx_cart_special",
|
||||
f"cp genesis_plus_gx_libretro.so {_TO}"
|
||||
"${PKG_NAME}_libretro.so",
|
||||
"genesis_plus_gx_cart_special"),),
|
||||
"geolith": (_lr("geolith", f"cp geolith_libretro.so {_TO}", "geolith"),),
|
||||
"gpsp": (_lr("gpsp", f"cp -v gpsp_libretro.so {_TO}", "gpsp"),),
|
||||
"gw-libretro": (_lr("gw-libretro", f"cp gw_libretro.so {_TO}", "gw"),),
|
||||
"handy": (_lr("handy", f"cp handy_libretro.so {_TO}", "handy"),),
|
||||
"hatari": (_lr("hatari", f"cp ../hatari_libretro.so {_TO}", "hatari"),),
|
||||
# line 65: the build's only .so is mame_libretro.so.
|
||||
"mame": (_lr("mame", f"cp *.so {_TO}", "mame"),),
|
||||
"mame2003-plus": (_lr("mame2003-plus", f"cp mame2003_plus_libretro.so {_TO}",
|
||||
"mame2003_plus"),),
|
||||
"mame2010": (_lr("mame2010", f"cp mame2010_libretro.so {_TO}", "mame2010"),),
|
||||
"meowpc98": (_lr("meowpc98", f"cp libretro/nekop2_libretro.so {_TO}", "nekop2"),),
|
||||
"mesen": (_lr("mesen", f"cp Libretro/mesen_libretro.so {_TO}", "mesen"),),
|
||||
"mesen-s": (_lr("mesen-s", f"cp Libretro/mesen-s_libretro.so {_TO}", "mesen-s"),),
|
||||
"mgba": (_lr("mgba", f"cp mgba_libretro.so {_TO}", "mgba"),),
|
||||
"mrboom": (_lr("mrboom", f"cp mrboom_libretro.so {_TO}", "mrboom"),),
|
||||
# PKG_ARCH="arm" (line 25): scripts/build skips it on aarch64.
|
||||
"mupen64plus": (_lr("mupen64plus", f"cp mupen64plus_libretro.so {_TO}"
|
||||
"mupen64plus_32b_libretro.so", "mupen64plus_32b"),),
|
||||
"mupen64plus-nx": (_lr("mupen64plus-nx", f"cp mupen64plus_next_libretro.so {_TO}",
|
||||
"mupen64plus_next"),),
|
||||
"mupen64plus-nx-alt": (_lr("mupen64plus-nx-alt", f"cp mupen64plus_next_libretro.so {_TO}"
|
||||
"mupen64plus_next_alt_libretro.so", "mupen64plus_next_alt"),),
|
||||
"neocd_libretro": (_lr("neocd_libretro", f"cp ${{PKG_BUILD}}/neocd_libretro.so {_TO}",
|
||||
"neocd"),),
|
||||
"nestopia": (_lr("nestopia", f"cp libretro/nestopia_libretro.so {_TO}", "nestopia"),),
|
||||
"np2kai": (_lr("np2kai", f"cp ${{PKG_BUILD}}/sdl/np2kai_libretro.so {_TO}", "np2kai"),),
|
||||
"nxengine": (_lr("nxengine", f"cp nxengine_libretro.so {_TO}", "nxengine"),),
|
||||
"o2em": (_lr("o2em", f"cp o2em_libretro.so {_TO}", "o2em"),),
|
||||
"opera": (_lr("opera", f"cp opera_libretro.so {_TO}", "opera"),),
|
||||
# lines 42-46: pcsx_rearmed_32b on arm, pcsx_rearmed elsewhere.
|
||||
"pcsx_rearmed": (
|
||||
_lr("pcsx_rearmed", "cp pcsx_rearmed_libretro.so "
|
||||
"${INSTALL}${INSTALLTO}/pcsx_rearmed_32b_libretro.so", "pcsx_rearmed_32b", arch="arm"),
|
||||
_lr("pcsx_rearmed", "cp pcsx_rearmed_libretro.so ${INSTALL}${INSTALLTO}",
|
||||
"pcsx_rearmed", arch="!arm"),
|
||||
),
|
||||
"picodrive": (_lr("picodrive", f"cp ${{PKG_BUILD}}/picodrive_libretro.so {_TO}",
|
||||
"picodrive"),),
|
||||
"pocketcdg": (_lr("pocketcdg", f"cp pocketcdg_libretro.so {_TO}", "pocketcdg"),),
|
||||
"pokemini": (_lr("pokemini", f"cp pokemini_libretro.so {_TO}", "pokemini"),),
|
||||
"potator": (_lr("potator", "cp ${PKG_BUILD}/platform/libretro/potator_libretro.so "
|
||||
f"{_TO}potator_libretro.so", "potator"),),
|
||||
"prboom": (_lr("prboom", f"cp prboom_libretro.so {_TO}prboom_libretro.so", "prboom"),),
|
||||
"prosystem": (_lr("prosystem", f"cp prosystem_libretro.so {_TO}", "prosystem"),),
|
||||
"puae": (_lr("puae", f"cp puae_libretro.so {_TO}", "puae"),),
|
||||
"puae2021": (_lr("puae2021", f"cp -v puae2021_libretro.so {_TO}", "puae2021"),),
|
||||
"px68k": (_lr("px68k", f"cp px68k_libretro.so {_TO}", "px68k"),),
|
||||
"reminiscence": (_lr("reminiscence", f"cp reminiscence_libretro.so {_TO}", "reminiscence"),),
|
||||
"same_cdi": (_lr("same_cdi", f"cp -v same_cdi_libretro.so {_TO}", "same_cdi"),),
|
||||
"sameboy": (_lr("sameboy", f"cp libretro/sameboy_libretro.so {_TO}", "sameboy"),),
|
||||
"sameduck": (_lr("sameduck", f"cp build/bin/sameduck_libretro.so {_TO}", "sameduck"),),
|
||||
"scummvm": (_lr("scummvm", 'cp "${PKG_BUILD}/backends/platform/libretro/scummvm_libretro."'
|
||||
"{so,info} ${INSTALL}/usr/lib/libretro/", "scummvm"),),
|
||||
"snes9x": (_lr("snes9x", f"cp libretro/snes9x_libretro.so {_TO}", "snes9x"),),
|
||||
"snes9x2002": (_lr("snes9x2002", f"cp snes9x2002_libretro.so {_TO}", "snes9x2002"),),
|
||||
"snes9x2005_plus": (_lr("snes9x2005_plus", f"cp snes9x2005_plus_libretro.so {_TO}",
|
||||
"snes9x2005_plus"),),
|
||||
"snes9x2010": (_lr("snes9x2010", f"cp snes9x2010_libretro.so {_TO}", "snes9x2010"),),
|
||||
"stella": (_lr("stella", f"cp ${{PKG_BUILD}}/src/os/libretro/stella_libretro.so {_TO}",
|
||||
"stella"),),
|
||||
"supafaust": (_lr("supafaust", f"cp -v mednafen_supafaust_libretro.so {_TO}",
|
||||
"mednafen_supafaust"),),
|
||||
"swanstation": (_lr("swanstation", "cp ${PKG_BUILD}/.${TARGET_NAME}/swanstation_libretro.so "
|
||||
f"{_TO}", "swanstation"),),
|
||||
"tgbdual": (_lr("tgbdual", f"cp tgbdual_libretro.so {_TO}", "tgbdual"),),
|
||||
"tic-80": (_lr("tic-80", "cp ${PKG_BUILD}/.${TARGET_NAME}/bin/tic80_libretro.so "
|
||||
f"{_TO}tic80_libretro.so", "tic80"),),
|
||||
"tyrquake": (_lr("tyrquake", f"cp tyrquake_libretro.so {_TO}", "tyrquake"),),
|
||||
# PKG_ARCH="arm" (line 25): scripts/build skips it on aarch64.
|
||||
"uae4arm": (_lr("uae4arm", f"cp uae4arm_libretro.so {_TO}uae4arm_32b_libretro.so",
|
||||
"uae4arm_32b"),),
|
||||
"uzem": (_lr("uzem", f"cp uzem_libretro.so {_TO}", "uzem"),),
|
||||
"vba-next": (_lr("vba-next", f"cp vba_next_libretro.so {_TO}", "vba_next"),),
|
||||
"vbam": (_lr("vbam", f"cp ../src/libretro/vbam_libretro.so {_TO}", "vbam"),),
|
||||
"vecx": (_lr("vecx", f"cp vecx_libretro.so {_TO}vecx_libretro.so", "vecx"),),
|
||||
# line 55: make_target builds one vice_<emu>_libretro.so per emulator it
|
||||
# loops over (lines 37-50), x64, x64sc, x128, xplus4 and xvic among them.
|
||||
"vice": (_lr("vice", f"cp built/vice_*_libretro.so {_TO}",
|
||||
"vice_x128", "vice_x64", "vice_x64sc", "vice_xplus4", "vice_xvic"),),
|
||||
"virtualjaguar": (_lr("virtualjaguar", f"cp virtualjaguar_libretro.so {_TO}",
|
||||
"virtualjaguar"),),
|
||||
"xmil": (_lr("xmil", f"cp ${{PKG_BUILD}}/libretro/x1_libretro.so {_TO}", "x1"),),
|
||||
"xrick": (_lr("xrick", f"cp xrick_libretro.so {_TO}", "xrick"),),
|
||||
"yabasanshiro": (_lr("yabasanshiro", f"cp yabause/src/libretro/yabasanshiro_libretro.so {_TO}",
|
||||
"yabasanshiro"),),
|
||||
"lib32-flycast": (_lib32("lib32-flycast", "cp -va flycast_libretro.so "
|
||||
f"{_TO}flycast_32b_libretro.so", "flycast_32b"),),
|
||||
"lib32-mupen64plus": (_lib32("lib32-mupen64plus", "cp mupen64plus_libretro.so "
|
||||
f"{_TO}mupen64plus_32b_libretro.so", "mupen64plus_32b"),),
|
||||
"lib32-parallel-n64": (_lib32("lib32-parallel-n64", "cp parallel_n64_libretro.so "
|
||||
f"{_TO}parallel_n64_32b_libretro.so", "parallel_n64_32b"),),
|
||||
"lib32-pcsx_rearmed": (_lib32("lib32-pcsx_rearmed", "cp pcsx_rearmed_libretro.so "
|
||||
f"{_TO}pcsx_rearmed_32b_libretro.so", "pcsx_rearmed_32b"),),
|
||||
"lib32-uae4arm": (_lib32("lib32-uae4arm", "cp uae4arm_libretro.so "
|
||||
f"{_TO}uae4arm_32b_libretro.so", "uae4arm_32b"),),
|
||||
# Standalone emulators, by the program the runner starts.
|
||||
"PPSSPPSDL": (_sa("PPSSPPSDL", 'cp `find . -name "PPSSPPSDL" | xargs echo` '
|
||||
"${INSTALL}/usr/bin/PPSSPPSDL", "PPSSPPSDL"),),
|
||||
"advancemame": (_sa("advancemame", "cp -r ${PKG_BUILD}/obj/mame/linux/blend/advmame "
|
||||
"${INSTALL}/usr/bin", "AdvanceMame"),),
|
||||
"amiberry": (_sa("amiberry", "cp -a amiberry* ${INSTALL}/usr/bin/amiberry", "AMIBERRY"),),
|
||||
"dolphinSA": (_sa("dolphinSA", "cp -rf ${PKG_BUILD}/.${TARGET_NAME}/Binaries/"
|
||||
"dolphin-emu-nogui ${INSTALL}/usr/bin", "dolphin"),),
|
||||
# dosbox comes from the meson install, dosbox.start from line 28.
|
||||
"dosbox-staging": (_sa("dosbox-staging", "cp -a ${PKG_DIR}/scripts/* ${INSTALL}/usr/bin/",
|
||||
"DOSBOXSDL2"),),
|
||||
"duckstation": (_sa("duckstation", "cp -rf ${PKG_BUILD}/.${TARGET_NAME}/bin/"
|
||||
"duckstation-nogui ${INSTALL}/usr/bin", "Duckstation"),),
|
||||
"ecwolf": (_sa("ecwolf", "cp ${PKG_BUILD}/ecwolf ${INSTALL}/usr/bin/ecwolf", "ECWolf"),),
|
||||
"fbneoSA": (_sa("fbneoSA", "cp -rf ${PKG_BUILD}/fbneo ${INSTALL}/usr/bin", "FBNEOSA"),),
|
||||
"flycastsa": (_sa("flycastsa", "cp ${PKG_BUILD}/.${TARGET_NAME}/flycast "
|
||||
"${INSTALL}/usr/bin/flycast", "flycastsa"),),
|
||||
"hatarisa": (_sa("hatarisa", "cp src/hatari ${INSTALL}/usr/bin", "HATARISA"),),
|
||||
# hypseus comes from the cmake install (PKG_TOOLCHAIN, line 13).
|
||||
"hypseus-singe": (_sa("hypseus-singe", 'PKG_TOOLCHAIN="cmake"', "HYPSEUS"),),
|
||||
"ikemen-go": (_sa("ikemen-go", 'cp "$PKG_BUILD/bin/Ikemen_Go_"* "$INSTALL/usr/bin/Ikemen_Go"',
|
||||
"ikemen"),),
|
||||
"jzintv": (_sa("jzintv", "cp bin/jzintv ${INSTALL}/usr/bin", "jzintv"),),
|
||||
# The metapackage's dependencies (line 10); mupen64plus itself comes from
|
||||
# mupen64plussa-ui-console's makeinstall_target.
|
||||
"mupen64plussa": (
|
||||
_sa("mupen64plussa", 'PKG_DEPENDS_TARGET="toolchain mupen64plussa-core '
|
||||
"mupen64plussa-ui-console mupen64plussa-audio-sdl mupen64plussa-input-sdl "
|
||||
'mupen64plussa-rsp-hle mupen64plussa-video-rice mupen64plussa-video-glide64mk2"',
|
||||
"mupen64plussa"),
|
||||
Install("packages/sx05re/emulators/mupen64plussa/mupen64plussa-ui-console/package.mk",
|
||||
"cp ${PKG_BUILD}/projects/unix/mupen64plus ${UBINDIR}", ()),
|
||||
),
|
||||
"openbor": (_sa("openbor", 'cp `find . -name "OpenBOR.elf" | xargs echo` '
|
||||
"${INSTALL}/usr/bin/OpenBOR", "OPENBOR"),),
|
||||
# scummvm comes from the make install, moved to /usr/bin at line 51.
|
||||
"scummvmsa": (_sa("scummvmsa", "mv ${INSTALL}/usr/local/bin ${INSTALL}/usr/", "SCUMMVMSA"),),
|
||||
# solarus-run comes from the cmake install, solarus.sh from line 22.
|
||||
"solarus": (_sa("solarus", "cp ${PKG_DIR}/scripts/* ${INSTALL}/usr/bin", "Solarus"),),
|
||||
"stellasa": (_sa("stellasa", "cp -rf ${PKG_BUILD}/stella ${INSTALL}/usr/bin", "STELLASA"),),
|
||||
"yabasanshiroSA_1_11": (_sa("yabasanshiroSA_1_11", "cp -a ${PKG_BUILD}/src/retro_arena/"
|
||||
"yabasanshiro ${INSTALL}/usr/bin", "yabasanshiro"),),
|
||||
"lib32-bennugd-monolithic": (Install(
|
||||
"packages/lib32/emuelec/ports/lib32-bennugd-monolithic/package.mk",
|
||||
"cp ${PKG_BUILD}/projects/cmake/bgdi/bgdi ${INSTALL}/usr/bin", ("bgdi",)),),
|
||||
# The metapackage's own bin/ (line 82) carries pico8.sh, which starts the
|
||||
# PICO-8 binary the user copies to bios/pico-8.
|
||||
"emuelec": (Install(META_MK, "cp -rf ${PKG_DIR}/bin ${INSTALL}/usr", ("Pico-8",)),),
|
||||
}
|
||||
|
||||
# Packages of a device list whose install step puts no ES core on the image.
|
||||
NO_ES_CORE: dict[str, str] = {
|
||||
"toolchain": "build toolchain",
|
||||
"${OPENGLES}": "graphics driver",
|
||||
"emuelec-emulationstation": "the frontend",
|
||||
"retroarch": "the libretro frontend",
|
||||
"lib32-retroarch": "the 32-bit libretro frontend, retroarch32",
|
||||
"emuelec-tools": "system tools",
|
||||
"emuelec-32bit-info": "the .info files of the 32-bit cores",
|
||||
"lib32-essential": "32-bit runtime",
|
||||
"lib32-libusb": "32-bit library",
|
||||
"lib32-box86": "x86 translator",
|
||||
"fbterm": "terminal",
|
||||
"kmscon": "terminal",
|
||||
"odroidgoa-utils": "device tools and retrorun, a runner",
|
||||
"amiberry-lite": "amiberry-lite, which es_systems.json names nowhere",
|
||||
"yabasanshiroSA_1_5": "yabasanshiro1_5, which es_systems.json names nowhere",
|
||||
"lib32-droidports": "gmloader, which es_cores drops (_overrides.yml)",
|
||||
"gme": "gme_libretro.so, which es_systems.json names nowhere",
|
||||
"nestopiaCV": "nestopiaCV_libretro.so, which es_systems.json names nowhere",
|
||||
"quasi88": "quasi88_libretro.so, which es_systems.json names nowhere",
|
||||
"spring": "spring_*_libretro.so, which es_systems.json names nowhere",
|
||||
}
|
||||
|
||||
# ES core names no package of any device builds, by the package that would.
|
||||
# The port engines are dependencies of emuelec-ports (its package.mk,
|
||||
# lines 17-59), and no package of the image depends on emuelec-ports;
|
||||
# flycast-dojo is in no device list.
|
||||
UNBUILT: dict[str, str] = {
|
||||
**dict.fromkeys(
|
||||
("CGeniusExe", "Chocolate-Doom", "GZDoom", "LZDoom", "OpenJazz", "RigelEngine",
|
||||
"VVVVVV", "abuse", "bs", "bstone", "ccleste", "cdogs-sdl", "devilutionx",
|
||||
"eduke32", "fallout-ce", "fallout2-ce", "fheroes2", "hcl", "hode", "hurrican",
|
||||
"opentyrian", "prince", "smw", "sonic2013", "soniccd", "sonicmania", "supertux2",
|
||||
"supertuxkart", "xash3d"),
|
||||
"emuelec-ports",
|
||||
),
|
||||
"flycast_dojo": "flycast-dojo",
|
||||
}
|
||||
|
||||
|
||||
def release_devices(asset_names: list[str]) -> dict[str, str]:
|
||||
"""{device: architecture} of each image a release publishes."""
|
||||
found: dict[str, str] = {}
|
||||
for name in asset_names:
|
||||
match = _ASSET_RE.match(name)
|
||||
if match:
|
||||
found.setdefault(match.group("device"), match.group("arch"))
|
||||
if not found:
|
||||
raise RuntimeError("the EmuELEC release publishes no device image")
|
||||
return found
|
||||
|
||||
|
||||
def _require(text: str, lines: tuple[str, ...], where: str) -> None:
|
||||
present = {line.strip() for line in text.splitlines()}
|
||||
missing = [line for line in lines if line not in present]
|
||||
if missing:
|
||||
raise RuntimeError(f"{where} no longer reads {missing[0]!r}")
|
||||
|
||||
|
||||
def _quoted(text: str, name: str, where: str) -> list[str]:
|
||||
"""The words of `NAME="..."`; a backslash-newline inside the quotes is
|
||||
removed by bash, so the value reads as one line."""
|
||||
match = re.search(rf'^\s*{name}="([^"]*)"', text, re.MULTILINE)
|
||||
if match is None:
|
||||
raise RuntimeError(f"{where} sets no {name}")
|
||||
return match.group(1).replace("\\\n", " ").split()
|
||||
|
||||
|
||||
class Metapackage(NamedTuple):
|
||||
emus: list[str]
|
||||
experimental: list[str]
|
||||
s922x: list[str]
|
||||
lib32: list[str]
|
||||
|
||||
|
||||
def read_metapackage(package_mk: str, options: str) -> Metapackage:
|
||||
"""The lists the metapackage rules work on, with PKG_EMUS expanded."""
|
||||
_require(package_mk, _META_LINES, META_MK)
|
||||
emus = _quoted(package_mk, "PKG_EMUS", META_MK)
|
||||
if emus[:1] != ["${LIBRETRO_CORES}"]:
|
||||
raise RuntimeError(f"{META_MK}: PKG_EMUS no longer starts with ${{LIBRETRO_CORES}}")
|
||||
lib32 = _LIB32_RE.search(package_mk)
|
||||
if lib32 is None:
|
||||
raise RuntimeError(f"{META_MK}: no aarch64 lib32 list")
|
||||
return Metapackage(
|
||||
emus=_quoted(options, "LIBRETRO_CORES", OPTIONS) + emus[1:],
|
||||
experimental=_quoted(package_mk, "PKG_EXPERIMENTAL", META_MK),
|
||||
s922x=_quoted(options, "LIBRETRO_S922X_CORES", OPTIONS),
|
||||
lib32=lib32.group(1).replace("\\\n", " ").split(),
|
||||
)
|
||||
|
||||
|
||||
def _sed_first(words: str, pattern: str) -> str:
|
||||
"""`echo ${PKG_DEPENDS_TARGET} | sed "s|<pattern>| |"`: the unquoted echo
|
||||
joins the words with single spaces, sed replaces the first match only,
|
||||
anywhere in the line, a word's tail included."""
|
||||
line = " ".join(words.split())
|
||||
at = line.find(pattern)
|
||||
return line if at < 0 else f"{line[:at]} {line[at + len(pattern):]}"
|
||||
|
||||
|
||||
def device_packages(meta: Metapackage, device: str, arch: str) -> list[str]:
|
||||
"""PKG_DEPENDS_TARGET of the metapackage for one device (lines 8-77)."""
|
||||
depends = "toolchain ${OPENGLES} emuelec-emulationstation retroarch"
|
||||
depends += " emuelec-tools " + " ".join(meta.emus) + " " + " ".join(meta.experimental)
|
||||
if device in _BIG:
|
||||
depends += " " + " ".join(meta.s922x)
|
||||
if device in _HANDHELD:
|
||||
depends += " kmscon odroidgoa-utils"
|
||||
for discore in ("duckstation", "mesen-s", "virtualjaguar", "quicknes", "MC"):
|
||||
depends = _sed_first(depends, f"{discore} ")
|
||||
depends += " yabasanshiro"
|
||||
else:
|
||||
depends += " fbterm"
|
||||
if arch == "aarch64":
|
||||
for discore in ("quicknes", "parallel-n64", "pcsx_rearmed"):
|
||||
depends = _sed_first(depends, discore)
|
||||
depends += " " + " ".join(meta.lib32)
|
||||
if device in _BIG:
|
||||
depends += " dolphinSA"
|
||||
if device == "Amlogic-old":
|
||||
for discore in ("yabasanshiroSA_1_11", "yabasanshiroSA_1_5", "yabasanshiro",
|
||||
"same_cdi", "duckstation"):
|
||||
depends = _sed_first(depends, f"{discore} ")
|
||||
if device in _BIG:
|
||||
depends += " mame"
|
||||
if device in ("RK356x", "OdroidM1"):
|
||||
depends = _sed_first(depends, "flycast-dojo")
|
||||
return depends.split()
|
||||
|
||||
|
||||
def arch_builds(pkg_arch: str, arch: str) -> bool:
|
||||
"""scripts/build:28-30: an empty PKG_ARCH builds everywhere, `!<arch>`
|
||||
excludes, otherwise the list names the arch or `any`."""
|
||||
words = pkg_arch.split()
|
||||
if not words:
|
||||
return True
|
||||
if f"!{arch}" in words:
|
||||
return False
|
||||
return arch in words or "any" in words
|
||||
|
||||
|
||||
def pkg_arch(package_mk: str) -> str:
|
||||
match = re.search(r'^PKG_ARCH="([^"]*)"', package_mk, re.MULTILINE)
|
||||
return match.group(1) if match else ""
|
||||
|
||||
|
||||
def es_systems_for(es_json: str, es_mk: str, device: str) -> SystemEmulators:
|
||||
"""es_systems.json as es_systems.cfg reads after makeinstall_target's
|
||||
deletions on one device (emuelec-emulationstation/package.mk:114-143).
|
||||
|
||||
`sed "s|<core>X</core>||g"` matches a core written without attributes:
|
||||
generate_es_systems.py gives `default="true"` to the first default core
|
||||
of a system and `incompatible_extensions` to a core listing them.
|
||||
"""
|
||||
_require(es_mk, _ES_LINES, ES_MK)
|
||||
if device in _HANDHELD:
|
||||
remove_cores = {"mesen-s", "quicknes", "mame2016", "mesen"}
|
||||
drop_systems: set[str] = set()
|
||||
drop_emulators = {"Duckstation"}
|
||||
elif device == "Amlogic-old":
|
||||
remove_cores = {"mesen-s", "quicknes", "mame2016", "mesen", "yabasanshiroSA",
|
||||
"yabasanshiro"}
|
||||
drop_systems = {"saturn", "phillips-cdi"}
|
||||
drop_emulators = {"Duckstation", "retrorun"}
|
||||
else:
|
||||
# Line 117 holds for every device: no name is all three at once.
|
||||
remove_cores = set()
|
||||
drop_systems = set()
|
||||
drop_emulators = {"retrorun"}
|
||||
systems: SystemEmulators = {}
|
||||
for system in json.loads(es_json).get("systems", []):
|
||||
if system["name"] in drop_systems:
|
||||
continue
|
||||
default_set = False
|
||||
emulators = []
|
||||
for emulator in system.get("emulators") or []:
|
||||
kept = []
|
||||
for core in emulator.get("cores") or []:
|
||||
bare = "incompatible" not in core
|
||||
if core.get("default", False) and not default_set:
|
||||
default_set = True
|
||||
bare = False
|
||||
if not (bare and core["name"] in remove_cores):
|
||||
kept.append(core["name"])
|
||||
if emulator["name"] not in drop_emulators:
|
||||
emulators.append((emulator["name"], kept))
|
||||
systems[system["name"]] = emulators
|
||||
return systems
|
||||
|
||||
|
||||
def device_cores(
|
||||
packages: list[str],
|
||||
arch: str,
|
||||
archs: dict[str, str],
|
||||
systems: SystemEmulators,
|
||||
) -> list[str]:
|
||||
"""ES core names a device both installs and offers.
|
||||
|
||||
`archs` maps each package of PROVIDERS to its PKG_ARCH.
|
||||
"""
|
||||
unknown = sorted(
|
||||
p for p in set(packages) if p not in PROVIDERS and p not in NO_ES_CORE
|
||||
and p not in UNBUILT.values()
|
||||
)
|
||||
if unknown:
|
||||
raise RuntimeError(f"{META_MK}: no install step read for {', '.join(unknown)}")
|
||||
unbuilt = sorted(set(packages) & set(UNBUILT.values()))
|
||||
if unbuilt:
|
||||
raise RuntimeError(f"{META_MK}: {unbuilt[0]} is built and its cores are read as absent")
|
||||
installed: set[str] = set()
|
||||
for package in [*set(packages), "emuelec"]:
|
||||
if package not in PROVIDERS or not arch_builds(archs[package], arch):
|
||||
continue
|
||||
for install in PROVIDERS[package]:
|
||||
if install.arch is None or (install.arch == arch) or (
|
||||
install.arch.startswith("!") and install.arch[1:] != arch
|
||||
):
|
||||
installed.update(install.cores)
|
||||
offered, _standalone, _dual = es_cores(
|
||||
systems, RUNNERS, BUILDS_OF, _not_cores(PLATFORM_NAME)
|
||||
)
|
||||
return sorted(installed & set(offered))
|
||||
|
||||
|
||||
def check_names(systems: SystemEmulators) -> None:
|
||||
"""Every ES core name is installed by a package read or known unbuilt."""
|
||||
offered, _standalone, _dual = es_cores(
|
||||
systems, RUNNERS, BUILDS_OF, _not_cores(PLATFORM_NAME)
|
||||
)
|
||||
provided = {core for installs in PROVIDERS.values() for i in installs for core in i.cores}
|
||||
unread = sorted(set(offered) - provided - set(UNBUILT))
|
||||
if unread:
|
||||
raise RuntimeError(f"es_systems.json names {', '.join(unread)}, no package read installs it")
|
||||
|
||||
|
||||
def check_installs(texts: dict[str, str]) -> dict[str, str]:
|
||||
"""{package: PKG_ARCH}, after checking each cited install line is there."""
|
||||
archs: dict[str, str] = {}
|
||||
for package, installs in PROVIDERS.items():
|
||||
for install in installs:
|
||||
_require(texts[install.path], (install.line,), install.path)
|
||||
archs[package] = pkg_arch(texts[installs[0].path])
|
||||
return archs
|
||||
|
||||
|
||||
class Scraper(BaseTargetScraper):
|
||||
"""Per-device core lists from EmuELEC's metapackage."""
|
||||
|
||||
def __init__(self, url: str = "https://github.com/" + REPO) -> None:
|
||||
super().__init__(url=url)
|
||||
self.ref = ""
|
||||
|
||||
def _raw(self, path: str) -> str:
|
||||
return fetch_bytes(RAW.format(ref=self.ref, path=path)).decode("utf-8")
|
||||
|
||||
def fetch_targets(self) -> dict:
|
||||
self.ref = self.pinned_revision(PLATFORM_NAME)
|
||||
release = json.loads(
|
||||
fetch_bytes(RELEASE_API.format(ref=self.ref), headers=github_headers())
|
||||
)
|
||||
devices = release_devices([asset["name"] for asset in release.get("assets", [])])
|
||||
meta = read_metapackage(self._raw(META_MK), self._raw(OPTIONS))
|
||||
paths = sorted({i.path for installs in PROVIDERS.values() for i in installs})
|
||||
archs = check_installs({path: self._raw(path) for path in paths})
|
||||
es_json = fetch_bytes(ES_SYSTEMS_TEMPLATE.format(ref=self.ref)).decode("utf-8")
|
||||
ports = port_engines(fetch_bytes(PORTS_TEMPLATE.format(ref=self.ref)).decode("utf-8"))
|
||||
es_mk = self._raw(ES_MK)
|
||||
check_names(with_ports(es_systems_for(es_json, es_mk, "Amlogic-ng"), ports))
|
||||
targets: dict[str, dict] = {}
|
||||
for device, arch in sorted(devices.items()):
|
||||
systems = with_ports(es_systems_for(es_json, es_mk, device), ports)
|
||||
cores = device_cores(device_packages(meta, device, arch), arch, archs, systems)
|
||||
targets[device.lower()] = {"architecture": arch, "cores": cores}
|
||||
print(f" {device.lower()} ({arch}): {len(cores)} cores", file=sys.stderr)
|
||||
return {
|
||||
"platform": PLATFORM_NAME,
|
||||
"source": f"{self.url}/blob/{self.ref}/{META_MK}",
|
||||
"devices_source": release["html_url"],
|
||||
"scraped_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"targets": targets,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False, description="Scrape EmuELEC per-device cores"
|
||||
)
|
||||
parser.add_argument("--dry-run", action="store_true", help="Show target summary")
|
||||
parser.add_argument("--output", "-o", help="Output YAML file")
|
||||
args = parser.parse_args()
|
||||
|
||||
scraper = Scraper()
|
||||
data = scraper.fetch_targets()
|
||||
if args.dry_run:
|
||||
for name, info in data["targets"].items():
|
||||
print(f" {name} ({info['architecture']}): {len(info['cores'])} cores")
|
||||
return
|
||||
if args.output:
|
||||
scraper.write_output(data, args.output)
|
||||
print(f"Written to {args.output}")
|
||||
return
|
||||
print(yaml.dump(data, default_flow_style=False, sort_keys=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user