feat: add AmberELEC hardware targets

AmberELEC's metapackage adds LIBRETRO_CORES_EXTRA (bsnes, Mesen, melonDS, geolith) only on RG552, RG353 and RG503, and deletes those cores from es_systems.cfg on every other device (packages/amberelec/package.mk:23-31, 45-58 at a21975bf). The new target scraper reads the Makefile's device images, that test and es_systems.cfg the way the platform scraper does: RG351P, RG351V and RG351MP list 125 cores, the other three 136. The installer reads the device from COREELEC_DEVICE in os-release (scripts/image:168).
This commit is contained in:
Abdessamad Derraz committed 2026-11-06 09:04:51 +01:00
1 parent 8107564631
commit 43cb0f84ab
11 files changed
+1895 -18

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
"""AmberELEC's images differ by device, and only its metapackage says how.
packages/amberelec/package.mk adds LIBRETRO_CORES_EXTRA (melonDS, bsnes,
Mesen, geolith) on RG552, RG353 and RG503 and deletes those cores from
es_systems.cfg everywhere else. With no target list, an RG351P pack carried
the firmware of cores its image never had.
"""
from __future__ import annotations
import sys
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
from scraper.targets import amberelec_targets_scraper as scraper # noqa: E402
MAKEFILE = """RG351P:
\tDEVICE=RG351P ARCH=aarch64 ./scripts/build_distro
RG552:
\tDEVICE=RG552 ARCH=aarch64 ./scripts/build_distro
toolchain-RK3326:
\tDEVICE=RK3326 ARCH=aarch64 ./scripts/build toolchain
"""
PACKAGE_MK = """ LIBRETRO_CORES_EXTRA="bsnes melonds"
PKG_EMUS="${LIBRETRO_CORES}"
if [[ "${DEVICE}" == "RG552" || "${DEVICE}" =~ ^(RG353|RG503)$ ]]; then
PKG_EMUS+=" ${LIBRETRO_CORES_EXTRA}"
fi
makeinstall_target() {
for CORE in ${LIBRETRO_CORES_EXTRA}; do
if [[ $CORE == "melonds" ]]; then
sed -i -e '/<emulator name/{:a;N;/<\\/emulator>/!ba;/melonds/d}' es_systems.cfg
fi
sed -i "s|<core>$CORE</core>||g" es_systems.cfg
done
}
"""
ES_SYSTEMS = """<systemList>
<system>
<name>snes</name>
<emulators>
<emulator name="retroarch">
<cores>
<core default="true">snes9x</core>
<core>bsnes</core>
</cores>
</emulator>
</emulators>
</system>
<system>
<name>nds</name>
<emulators>
<emulator name="retroarch">
<cores>
<core>melonds</core>
</cores>
</emulator>
<emulator name="drastic">
<cores>
<core default="true">drastic</core>
</cores>
</emulator>
</emulators>
</system>
</systemList>
"""
class TheMetapackageDecidesEachDevice(unittest.TestCase):
def test_devices_are_the_images_the_makefile_builds(self):
self.assertEqual(scraper.devices(MAKEFILE), {"RG351P": "aarch64", "RG552": "aarch64"})
def test_the_extra_cores_and_their_devices(self):
extra, keep = scraper.extra_cores(PACKAGE_MK)
self.assertEqual(extra, ["bsnes", "melonds"])
self.assertEqual(keep, {"RG552", "RG353", "RG503"})
def test_a_device_without_them_loses_their_cores(self):
extra, _keep = scraper.extra_cores(PACKAGE_MK)
full = scraper.device_cores(ES_SYSTEMS, extra, True)
lean = scraper.device_cores(ES_SYSTEMS, extra, False)
self.assertEqual(set(full) - set(lean), {"bsnes", "melonds"})
self.assertIn("drastic", lean)
def test_a_changed_removal_stops_the_scrape(self):
changed = PACKAGE_MK.replace('sed -i "s|<core>$CORE</core>||g"', "true")
with self.assertRaises(RuntimeError):
scraper.extra_cores(changed)
if __name__ == "__main__":
unittest.main()
+17 -5
View File
@@ -3420,7 +3420,8 @@ class ACardPreparedOnAComputerGetsItsBoard(unittest.TestCase):
class APlatformWithoutTargetsSaysSoOffline(unittest.TestCase):
"""Offline, --target on AmberELEC asked for install/targets/amberelec.json,
which exists nowhere: online the same run says it has no targets."""
which existed nowhere: online the same run said it had no targets.
EmuELEC has none still."""
def test_the_list_matches_the_published_targets(self):
published = {path.stem for path in (REPO_ROOT / "install" / "targets").glob("*.json")}
@@ -3428,16 +3429,16 @@ class APlatformWithoutTargetsSaysSoOffline(unittest.TestCase):
def test_offline_says_there_are_no_targets(self):
dest = Path(_tempdir(self))
for extra in ([], ["--manifest", str(REPO_ROOT / "install" / "amberelec.json")]):
for extra in ([], ["--manifest", str(REPO_ROOT / "install" / "emuelec.json")]):
with self.subTest(manifest=bool(extra)):
proc = subprocess.run(
[sys.executable, str(REPO_ROOT / "install.py"), "--platform", "amberelec",
[sys.executable, str(REPO_ROOT / "install.py"), "--platform", "emuelec",
"--dest", str(dest), "--target", "rk3326", "--offline", "--no-input",
"--dry-run", *extra],
capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL)
self.assertEqual(proc.returncode, 2, proc.stderr)
self.assertIn("AmberELEC has no hardware targets", proc.stderr)
self.assertNotIn("targets/amberelec.json", proc.stderr)
self.assertIn("EmuELEC has no hardware targets", proc.stderr)
self.assertNotIn("targets/emuelec.json", proc.stderr)
class TheBoardTheSystemNamesIsTheTarget(unittest.TestCase):
@@ -3492,6 +3493,17 @@ class TheBoardTheSystemNamesIsTheTarget(unittest.TestCase):
.read_text(encoding="utf-8"))
self.assertLessEqual({*install._RPI_MODELS.values(), "x86_64"}, set(targets))
def test_amberelec_names_its_device(self):
# An RG351P image carries no melonDS or bsnes, and the full list was
# installed: AmberELEC had no target list to read the device against.
with unittest.mock.patch.object(
install, "_parse_os_release", return_value={"COREELEC_DEVICE": "RG351P"}
):
self.assertEqual(install.detect_board("amberelec"), "rg351p")
targets = json.loads((REPO_ROOT / "install" / "targets" / "amberelec.json")
.read_text(encoding="utf-8"))
self.assertIn("rg351p", targets)
def test_lakka_names_its_device_or_its_project(self):
# An RPi4 Lakka installed 5.7 GB of every board's files: its board
# was in os-release and never read.