feat: narrow a knulli install to its board

This commit is contained in:
Abdessamad Derraz committed 2026-11-05 19:18:29 +01:00
1 parent 83f5d161ab
commit 289c180bfa
6 files changed
+56 -52

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
# and Invoke-WebRequest throws on a cut transfer before the hash is read:
# the site's build rewrites this line there (scripts/sitebootstrap.py).
$mismatchAdvice = "The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
$defaultInstallSha256 = "8760870a1e7e1f1b72379cfec88d595ac8d7729880af905e266b424e3ee306bc"
$defaultInstallSha256 = "ec117f8ed92e101b1adc51b1f07bf002840fb8e36c4c492617e1b48c47c6ee92"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+18 -28
View File
@@ -1450,36 +1450,28 @@ def _parse_bash_var(path: Path, key: str) -> str | None:
# recalbox-system.mk RECALBOX_SYSTEM_PLATFORM, ROCKNIX's scripts/image puts
# HW_DEVICE in os-release (RK3326, our rk3326). Lakka's scripts/image writes
# LIBREELEC_DEVICE when the image is built for a device (RPi4) and always
# LIBREELEC_PROJECT (Generic): the first one set is the board.
# LIBREELEC_PROJECT (Generic): the first one set is the board. KNULLI moves
# /usr/share/batocera to /usr/share/knulli (knulli-linux 57afa662,
# board/scripts/post-build-script.sh:264-278) and writes KNULLI_SYSTEM_ARCH
# to knulli.arch (package/system/knulli-system/knulli-system.mk:98-99).
_BOARD_SOURCES: dict[str, tuple[str, str]] = {
"batocera": ("file", "/usr/share/batocera/batocera.arch"),
"recalbox": ("file", "/recalbox/recalbox.arch"),
"rocknix": ("os_release", "HW_DEVICE"),
"lakka": ("os_release", "LIBREELEC_DEVICE|LIBREELEC_PROJECT"),
"knulli": ("file", "/usr/share/knulli/knulli.arch"),
}
# A platform that names its board without publishing a core list per board.
# Knulli moves /usr/share/batocera to /usr/share/knulli (knulli-linux
# 57afa662, board/scripts/post-build-script.sh:264-278) and writes its board
# to knulli.arch (package/system/knulli-system/knulli-system.mk:98-99).
_DERIVATIVE_BOARDS: dict[str, tuple[str, str]] = {
"knulli": ("Knulli", "/usr/share/knulli/knulli.arch"),
# The board a system's own name stands for, where the two differ: the
# target lists are named for the board files (configs/knulli-rk3566.board),
# and the arch file for the TARGET flag the board sets. knulli-rk3566.board
# sets BR2_PACKAGE_BATOCERA_TARGET_RK3568, which knulli-system.mk:22-23
# (20260511) writes as rk3568.
_BOARD_NAMES: dict[str, dict[str, str]] = {
"knulli": {"rk3568": "rk3566"},
}
def derivative_board(platform: str) -> tuple[str, str] | None:
"""(derivative, board) when a derivative of the platform names its board."""
name, where = _DERIVATIVE_BOARDS.get(platform, ("", ""))
if not where:
return None
try:
board = Path(where).read_text(encoding="utf-8", errors="replace").strip().lower()
except OSError:
return None
return (name, board) if board else None
# The board a card names on a partition a computer reads. Batocera writes
# its image's board to boot/batocera.board on the BATOCERA boot partition,
# the FAT one beside SHARE (batocera-43.1, board/batocera/scripts/
@@ -1520,7 +1512,11 @@ def detect_board(platform: str) -> str | None:
value = next((release[key] for key in where.split("|") if release.get(key)), "")
else:
return None
return value.strip().lower() or None
board = value.strip().lower()
# A board no TARGET line names is written "unknown" (KNULLI's r16).
if board == "unknown":
return None
return _BOARD_NAMES.get(platform, {}).get(board, board) or None
def _parse_json_path(path: Path, *keys: str) -> str | None:
@@ -12989,7 +12985,7 @@ def build_parser() -> argparse.ArgumentParser:
"--target",
metavar="NAME",
help="hardware target for core filtering (switch, rpi4, ...). "
"On Batocera, Recalbox, ROCKNIX and Lakka the board is read from the system",
"On Batocera, Recalbox, ROCKNIX, Lakka and KNULLI the board is read from the system",
)
parser.add_argument(
"--every-board",
@@ -15064,12 +15060,6 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
print(f" {words}")
EVENTS.emit("board", platform=plat_name, board=board,
applied=target == board, message=words)
else:
derived = derivative_board(plat_name)
if derived:
print(f" {derived[0]} board {derived[1]} detected. {derived[0]} "
"builds its own set of cores and publishes no core list "
"per board: installing the files of every board")
if target:
targets = target_lists.get(plat_name)
if targets is None:
+1 -1
View File
@@ -23,7 +23,7 @@ DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/ins
# curl or wget stop on a cut transfer before the hash is read: the site's
# build rewrites this line there (scripts/sitebootstrap.py).
MISMATCH_ADVICE="The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
DEFAULT_INSTALL_SHA256="8760870a1e7e1f1b72379cfec88d595ac8d7729880af905e266b424e3ee306bc"
DEFAULT_INSTALL_SHA256="ec117f8ed92e101b1adc51b1f07bf002840fb8e36c4c492617e1b48c47c6ee92"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+8 -8
View File
@@ -27,8 +27,8 @@
{
"dest": "roms/ports/retrobios/install.py",
"repo_path": "install.py",
"size": 1090639,
"sha256": "8760870a1e7e1f1b72379cfec88d595ac8d7729880af905e266b424e3ee306bc",
"size": 1090336,
"sha256": "ec117f8ed92e101b1adc51b1f07bf002840fb8e36c4c492617e1b48c47c6ee92",
"executable": false
},
{
@@ -71,8 +71,8 @@
"muos": {
"archive": {
"dest": "ARCHIVE/RetroBIOS.muxapp",
"size": 1144266,
"sha256": "1b9ae46c0bbdbb75aaa2120dcce145cea9a15a971f272f44dd556a48e6b7098c"
"size": 1143963,
"sha256": "c99324398d6e3746ef65b3cf491c30defae5556664bb4e7cca1675c3d1c2a444"
},
"members": [
{
@@ -92,8 +92,8 @@
{
"name": "RetroBIOS/install.py",
"repo_path": "install.py",
"size": 1090639,
"sha256": "8760870a1e7e1f1b72379cfec88d595ac8d7729880af905e266b424e3ee306bc",
"size": 1090336,
"sha256": "ec117f8ed92e101b1adc51b1f07bf002840fb8e36c4c492617e1b48c47c6ee92",
"executable": false
},
{
@@ -166,8 +166,8 @@
{
"dest": ".config/modules/retrobios/install.py",
"repo_path": "install.py",
"size": 1090639,
"sha256": "8760870a1e7e1f1b72379cfec88d595ac8d7729880af905e266b424e3ee306bc",
"size": 1090336,
"sha256": "ec117f8ed92e101b1adc51b1f07bf002840fb8e36c4c492617e1b48c47c6ee92",
"executable": false
},
{
+22 -8
View File
@@ -5400,20 +5400,34 @@ class BatoceraAndKnulliByWhatTheirBuildsWrite(unittest.TestCase):
[("emuelec", Path("/storage/roms"))])
self.assertEqual(self._detect({"NAME": "CoreELEC", "ID": "coreelec"}, ()), [])
def test_a_knulli_board_is_named_and_not_narrowed(self):
board = Path(_tempdir(self)) / "knulli.arch"
board.write_text("a133")
with unittest.mock.patch.dict(install._DERIVATIVE_BOARDS,
{"knulli": ("Knulli", str(board))}):
self.assertEqual(install.derivative_board("knulli"), ("Knulli", "a133"))
self.assertIsNone(install.derivative_board("recalbox"))
def test_a_knulli_board_narrows_to_its_target(self):
"""KNULLI publishes a core list per board since 2026-11-05, and an
H700 handheld still took every board's files (9.3 GB for 4.5)."""
arch = Path(_tempdir(self)) / "knulli.arch"
targets = json.loads((REPO_ROOT / "install" / "targets" / "knulli.json")
.read_text(encoding="utf-8"))
for written, board in (("h700", "h700"), ("rk3568", "rk3566"), ("unknown", None)):
arch.write_text(written)
with self.subTest(written=written), unittest.mock.patch.dict(
install._BOARD_SOURCES, {"knulli": ("file", str(arch))}):
self.assertEqual(install.detect_board("knulli"), board)
if board:
self.assertIn(board, targets)
def test_each_board_name_is_a_published_target(self):
for plat, names in install._BOARD_NAMES.items():
targets = json.loads((REPO_ROOT / "install" / "targets" / f"{plat}.json")
.read_text(encoding="utf-8"))
for written, board in names.items():
with self.subTest(platform=plat, board=board):
self.assertIn(board, targets)
self.assertNotIn(written, targets)
def test_the_run_says_why_every_board_is_installed(self):
source = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
block = source[source.index("board = detect_board(plat_name)"):]
block = block[:block.index("if target:")]
self.assertIn("has no", block)
self.assertIn("derivative_board(plat_name)", block)
class RomMFirmwareFollowsItsTemplate(unittest.TestCase):
+6 -6
View File
@@ -958,7 +958,7 @@ what the options name, everything by default.
|--------|--------|
| <span id="option-platform"></span>`--platform NAME` | Install for this platform instead of the detected one. A system `--list-platforms` names under its own files (`muos`, `arkos`) stands for the file list it gets, and the run says which. ES-DE, LaunchBox and Android, which keep no BIOS folder, are answered with the run that fills each emulator's folder. Unknown names are refused with the closest ones |
| <span id="option-dest"></span>`--dest PATH` | Destination directory, overriding detection. Without `--platform`, the file list is the platform detected here: at a terminal the installer asks, Enter taking the one found, and otherwise it uses the one found, RetroArch's when nothing is found, and refuses when several are. The run prints whose list it is. A folder that does not exist is named with the nearest one that does, then created, except for a system that runs from its SD card, which refuses it. When more than its last level is missing (a mistyped path), a run at a terminal asks first, and the answer defaults to no |
| <span id="option-target"></span>`--target NAME` | Keep only the files the cores of that hardware target need, and the systems they emulate. An unknown target is refused rather than ignored, since carrying on would install everything. On Batocera, Recalbox, ROCKNIX and Lakka the board is read from the system (`batocera.arch`, `recalbox.arch`, `HW_DEVICE`, `LIBREELEC_DEVICE` or `LIBREELEC_PROJECT`) and applied when no `--target` is given, from a pack alone too when it carries the core list per board. On a computer, the board a Batocera card names on its BATOCERA partition (`boot/batocera.board`) is applied the same way, in line mode and on the full screen |
| <span id="option-target"></span>`--target NAME` | Keep only the files the cores of that hardware target need, and the systems they emulate. An unknown target is refused rather than ignored, since carrying on would install everything. On Batocera, Recalbox, ROCKNIX, Lakka and KNULLI the board is read from the system (`batocera.arch`, `recalbox.arch`, `HW_DEVICE`, `LIBREELEC_DEVICE` or `LIBREELEC_PROJECT`, `knulli.arch`) and applied when no `--target` is given, from a pack alone too when it carries the core list per board. On a computer, the board a Batocera card names on its BATOCERA partition (`boot/batocera.board`) is applied the same way, in line mode and on the full screen |
| <span id="option-every-board"></span>`--every-board` | Install the files of every board instead of the one the system names, for a card that moves between devices |
| <span id="option-installed-cores"></span>`--installed-cores` | Keep only what the cores already installed read (RetroArch, Lakka, RetroPie). See [Installed cores](#installed-cores) |
| <span id="option-cores-dir"></span>`--cores-dir PATH` | The folder RetroArch loads its cores from, for `--installed-cores` when it is not found. Implies `--installed-cores` |
@@ -1058,12 +1058,12 @@ AmberELEC, EmuELEC and KNULLI install the BIOS list they publish themselves. The
image or source of its Odroid Go builds is published, so neither its marker
files nor its folder could be read.
Batocera, Recalbox, ROCKNIX and Lakka also name the board they were built for, and
that board narrows the install unless `--target` or `--every-board` says
Batocera, Recalbox, ROCKNIX, Lakka and KNULLI also name the board they were built
for, and that board narrows the install unless `--target` or `--every-board` says
otherwise (see the options above). A board with no core list of its own is
named and gets the files of every board. KNULLI names its board in
`/usr/share/knulli/knulli.arch` but publishes no core list per board, so the
run says so and installs the files of every board.
named and gets the files of every board. KNULLI writes its board to
`/usr/share/knulli/knulli.arch` under the name of the chip its board file
selects: `rk3568` there is the `rk3566` list.
Some platforms keep their BIOS in a fixed directory, and that directory is the
evidence. They are looked for after the configurations below, for a platform