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.
This commit is contained in:
Abdessamad Derraz committed 2026-11-11 11:57:12 +01:00
1 parent b29500846b
commit 8770b9c067
4 files changed
+144 -32

No files matched your search

+37 -4
View File
@@ -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:
+19 -2
View File
@@ -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)