From 8770b9c067bb09de4dfcc185dd78f7e0684d3e14 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 11 Nov 2026 11:57:12 +0100 Subject: [PATCH] fix: install emuelec configs from the boot copy duckstation.sh quotes its glob, so its copy names a file called * and installs nothing. userconfig-setup copies /usr/config into /storage/.config at every boot, an existing file kept (false | cp -i), and /emuelec links there: that copy installs settings.ini and advmame.rc. A quoted glob is no install, and a redirection ends the copy's operands. Rescraped at v4.8: only these installs change. --- platforms/emuelec.yml | 67 ++++++++++++++++++---------- scripts/scraper/emuelec_launchers.py | 41 +++++++++++++++-- scripts/scraper/shell_launchers.py | 21 ++++++++- tests/test_launchers_emuelec.py | 47 +++++++++++++++++-- 4 files changed, 144 insertions(+), 32 deletions(-) diff --git a/platforms/emuelec.yml b/platforms/emuelec.yml index 3c181e6a4..01167303e 100644 --- a/platforms/emuelec.yml +++ b/platforms/emuelec.yml @@ -4295,8 +4295,8 @@ launchers: \ PathPAL unset" fails_when_missing: false installs: - path: /emuelec/configs/duckstation/settings.ini - source_ref: "packages/sx05re/emulators/duckstation/scripts/duckstation.sh:15" + path: /storage/.config/emuelec/configs/duckstation/settings.ini + source_ref: "packages/sx05re/emulators/duckstation/package.mk:56, packages/sysutils/systemd/scripts/userconfig-setup:12" kept_if_present: true overrides: [] source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:207-209, packages/sx05re/emulators/duckstation/config/settings.ini:6-9,\ @@ -4372,7 +4372,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4391,7 +4392,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4409,7 +4411,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4428,7 +4431,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4446,7 +4450,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4465,7 +4470,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4483,7 +4489,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4502,7 +4509,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4520,7 +4528,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4539,7 +4548,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4557,7 +4567,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4576,7 +4587,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4594,7 +4606,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4613,7 +4626,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4631,7 +4645,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4650,7 +4665,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4667,7 +4683,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4684,7 +4701,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4703,7 +4721,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4721,7 +4740,8 @@ launchers: depends_on_option: system != arcade installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" @@ -4740,7 +4760,8 @@ launchers: fails_when_missing: false installs: path: /emuelec/configs/advmame/advmame.rc - source_ref: "packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" + source_ref: "packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sysutils/systemd/scripts/userconfig-setup:12,\ + \ packages/sx05re/emulators/advancemame/bin/advmame.sh:17, 21" kept_if_present: false overrides: - "packages/sx05re/emulators/advancemame/bin/advmame.sh:27" diff --git a/scripts/scraper/emuelec_launchers.py b/scripts/scraper/emuelec_launchers.py index e52a23fc9..98388983e 100644 --- a/scripts/scraper/emuelec_launchers.py +++ b/scripts/scraper/emuelec_launchers.py @@ -40,6 +40,10 @@ EMULATORS = f"{SX05RE}/emulators" LIBRETRO = f"{SX05RE}/libretro" RETROARCH_MK = f"{SX05RE}/libretro_base/retroarch/package.mk" BIOS_ROOT = "/storage/roms/bios" +EMUELEC_MK = f"{SX05RE}/emuelec/package.mk" +# The copy of every package's /usr/config into /storage/.config at each boot, +# an existing file kept (cp -i answered by false). +USERCONFIG = "packages/sysutils/systemd/scripts/userconfig-setup" # The roms root that holds it (packages/sx05re/libretro_base/retroarch/package.mk:112,117). ROMS_ROOT = "/storage/roms" @@ -106,6 +110,19 @@ class Runs: return (RUN_EMU, set(self.of(name).lines)) +def _storage_path(tree: RepoTree, target: str) -> str: + """target with the /emuelec link resolved to the folder it points at. + + The emuelec package links /emuelec to /storage/.config/emuelec, where + the first-boot copy of /usr/config lands. + """ + mk = _read(tree, EMUELEC_MK) + _line, link = mk.find(r"ln -sf (/storage/\S+) \$\{INSTALL\}(/emuelec)$", "/emuelec link") + if target == link.group(2) or target.startswith(link.group(2) + "/"): + return link.group(1) + target[len(link.group(2)):] + return target + + def _evidence( record: Launcher, script: Script, @@ -113,15 +130,28 @@ def _evidence( keys: list[str], source_name: str = "", rewrites: Iterable[int] = (), + tree: RepoTree | None = None, + installed: Iterable[tuple[str, Iterable[int]]] = (), ) -> Launcher: - """A config record with the start script lines that put its file in place. + """A config record with the lines that put its file in place: the + first-boot copy of /usr/config when ``tree`` is given, then the start + script's own. ``installed`` are the package.mk lines putting the file + in /usr/config. ``rewrites`` are lines the extractor read as rewriting the record's values without naming their keys (advmame.sh's sed of /roms/mame). """ - placement = config_placement(script, target, keys, source_name=source_name) + placements = [] + if tree is not None: + setup = _read(tree, USERCONFIG) + placements.append(config_placement( + setup, _storage_path(tree, target), required=False, sources=("/usr/config/",) + )) + placement = config_placement(script, target, keys, source_name=source_name, + required=not placements or not placements[0].install) placement.overrides |= set(rewrites) - fields = evidence_fields([placement]) + placements.append(placement) + fields = evidence_fields(placements, installed) record.installs = fields["installs"] record.kept_if_present = fields["kept_if_present"] record.overrides = fields["overrides"] @@ -356,6 +386,8 @@ def duckstation(tree: RepoTree, runs: Runs) -> list[Launcher]: script, script.expand("${CONFIG_DIR}/settings.ini", run_line, set()), [f"[{s}] {k}" for s, k in (folder_key, *file_keys)], + tree=tree, + installed=[(mk.path, {mk_line})], )] @@ -569,7 +601,8 @@ def advancemame(tree: RepoTree, runs: Runs) -> list[Launcher]: target = script.expand("${CONFIG_DIR}/advmame.rc", run_line, set()) def proved(record: Launcher, key: str) -> Launcher: - return _evidence(record, script, target, [key], rewrites={sed_line}) + return _evidence(record, script, target, [key], rewrites={sed_line}, tree=tree, + installed=[(mk.path, {mk_line for _, _, mk_line in configs})]) records = [] for key, folder in value: diff --git a/scripts/scraper/shell_launchers.py b/scripts/scraper/shell_launchers.py index d847b2666..e71936986 100644 --- a/scripts/scraper/shell_launchers.py +++ b/scripts/scraper/shell_launchers.py @@ -483,6 +483,15 @@ def _guarded(script: Script, number: int, target: str) -> bool: return False +def _quoted_glob(command: str) -> bool: + """Whether the source of a copy holds a "*" only inside quotes.""" + found = [w for w in words(command)[1:] if not w.raw.startswith("-")] + if len(found) < 2: + return False + raw = found[0].raw + return "*" in raw and "*" not in re.sub(r'"[^"]*"|\'[^\']*\'', "", raw) + + def config_placement( script: Script, target: str, @@ -511,12 +520,16 @@ def config_placement( args = [] for word in words(line[copy.start(1):])[1:]: # The command ends at a separator or a redirection. - if word.raw in {";", "&&", "||", "|"} or re.match(r"\d*>", word.raw): + if word.raw in {";", "&&", "||", "|"} or re.match(r"\d*>|&>", word.raw): break args.append(word.text) operands = [a for a in args if not a.startswith("-")] if not operands: continue + # A quoted "*" is a file named *, which no package installs: + # the copy does nothing (EmuELEC's duckstation.sh). + if _quoted_glob(line[copy.start(1):]): + continue destination = script.expand(operands[-1], number, set()).rstrip("/") source = script.expand(operands[0], number, set()).rstrip("/") hits = target == destination or target.startswith(destination + "/") or ( @@ -536,7 +549,11 @@ def config_placement( continue if sources and not source.startswith(sources): continue - skip = "-n" in args or "--ignore-existing" in args + # cp -i fed by false answers no to every overwrite. + prompted = any( + a.startswith("-") and not a.startswith("--") and "i" in a[1:] for a in args + ) and re.search(r"\bfalse\s*\|\s*$", line[:copy.start(1)]) + skip = "-n" in args or "--ignore-existing" in args or bool(prompted) else: skip = False install.add(number) diff --git a/tests/test_launchers_emuelec.py b/tests/test_launchers_emuelec.py index 630547701..8ab6fa3fe 100644 --- a/tests/test_launchers_emuelec.py +++ b/tests/test_launchers_emuelec.py @@ -229,11 +229,25 @@ DUCKSTATION_SH = text( CONFIG_DIR="/emuelec/configs/duckstation" duckstation-nogui -fullscreen -settings "${CONFIG_DIR}/settings.ini" -- "${1}" if [ ! -d "${CONFIG_DIR}" ]; then - cp -rf /usr/config/emuelec/configs/duckstation/* "${CONFIG_DIR}" + cp -rf "/usr/config/emuelec/configs/duckstation/*" "${CONFIG_DIR}" fi """ ) +# packages/sysutils/systemd/scripts/userconfig-setup:11-12 at v4.8. +USERCONFIG = text( + """ + # Copy config files, but don't overwrite - this should replace our sample files + false | cp -iRp /usr/config/* /storage/.config/ &>/dev/null +""" +) + +EMUELEC_MK = text( + """ + ln -sf /storage/.config/emuelec ${INSTALL}/emuelec +""" +) + def yabasanshiro_sh(program: str) -> str: return text( @@ -400,6 +414,8 @@ FILES = { f"{EMU}/duckstation/config/settings.ini": DUCKSTATION_INI, f"{EMU}/duckstation/package.mk": DUCKSTATION_MK, f"{EMU}/duckstation/scripts/duckstation.sh": DUCKSTATION_SH, + el.USERCONFIG: USERCONFIG, + el.EMUELEC_MK: EMUELEC_MK, el.YABASANSHIRO_SCRIPTS[0]: yabasanshiro_sh("yabasanshiro"), el.YABASANSHIRO_SCRIPTS[1]: yabasanshiro_sh("yabasanshiro1_5"), f"{EMU}/jzintv/scripts/jzintv.sh": JZINTV_SH, @@ -540,12 +556,37 @@ class StandaloneRecords(unittest.TestCase): (record,) = records(el.duckstation) self.assertEqual((record["kind"], record["search_dir"]), ("scans_dir", "/storage/roms/bios")) - def test_duckstation_config_is_kept_when_present(self): + def test_duckstation_config_comes_from_the_boot_copy(self): + """duckstation.sh quotes its glob: its copy names a file called *, so + the settings.ini in place is the one userconfig-setup copies from + /usr/config, an existing file kept (false | cp -i).""" (record,) = records(el.duckstation) - self.assertEqual(record["installs"]["path"], "/emuelec/configs/duckstation/settings.ini") + self.assertEqual( + record["installs"], + {"path": "/storage/.config/emuelec/configs/duckstation/settings.ini", + "source_ref": f"{EMU}/duckstation/package.mk:1, {el.USERCONFIG}:2"}, + ) self.assertIs(record["kept_if_present"], True) self.assertEqual(record["overrides"], []) + def test_an_unquoted_glob_in_the_start_script_installs_too(self): + unquoted = DUCKSTATION_SH.replace( + '"/usr/config/emuelec/configs/duckstation/*"', "/usr/config/emuelec/configs/duckstation/*" + ) + (record,) = records(el.duckstation, **{f"{EMU}/duckstation/scripts/duckstation.sh": unquoted}) + self.assertEqual(record["installs"]["path"], "/emuelec/configs/duckstation/settings.ini") + self.assertIs(record["kept_if_present"], True) + + def test_without_a_boot_copy_the_quoted_glob_installs_nothing(self): + with self.assertRaises(LauncherPatternMissing): + records(el.duckstation, **{el.USERCONFIG: "#!/bin/sh\n"}) + + def test_an_overwriting_boot_copy_does_not_keep_the_file(self): + (record,) = records(el.duckstation, **{ + el.USERCONFIG: "cp -Rp /usr/config/* /storage/.config/ &>/dev/null\n", + }) + self.assertIs(record["kept_if_present"], False) + def test_duckstation_with_a_fixed_bios_path_raises(self): changed = DUCKSTATION_INI.replace("PathPAL =", "PathPAL = /storage/roms/bios/scph5502.bin") with self.assertRaises(LauncherPatternMissing):