feat: list the names --emulator accepts

An unknown --emulator name pointed at --list-cores and counted 441 names
while --list-cores prints 189: the buildbot names --emulator accepts
(mednafen_psx for beetle_psx) were listed nowhere. --list-emulators prints
each accepted name beside the emulator it stands for, and the hint names
it.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 05:22:08 +02:00
1 parent 8a19d647a8
commit 253a41ebc8
5 files changed
+83 -12

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@
$ErrorActionPreference = "Stop"
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
$defaultInstallSha256 = "df46e48289c89c5c542095b01c16208ef54696df472685c99327e5022fcd7ab8"
$defaultInstallSha256 = "2409cff28ad4eed1546161aa5abd6808fcd6a010bda7943eb1076f4ab0559c9f"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+43 -9
View File
@@ -2408,7 +2408,8 @@ def resolve_choices(values: list[str], known: list[str], label: str) -> list[str
if unknown:
if not known:
raise ValueError(f"unknown {label}: {', '.join(unknown)}. This list records none.")
listing = "--list-systems" if label == "system" else "--list-cores"
listing = {"system": "--list-systems", "emulator": "--list-emulators"}.get(
label, "--list-cores")
raise ValueError(
f"unknown {label}: {', '.join(unknown)}. {did_you_mean(unknown, known, listing)}"
)
@@ -3923,6 +3924,33 @@ def _print_choices(
print(f" {name:<{width}} {_sized(held)}")
def emulator_names(manifest: dict, files: list[dict], omitted: list[dict]) -> dict[str, str]:
"""Every name --emulator accepts, with the profile each one stands for.
A profile answers to its own name and to the names its core is built
under (core_names: beetle_psx and mednafen_psx). A core named by a file
and by no profile stands for itself.
"""
names = {core: core for core in available_choices(files, omitted)["cores"]}
for profile, aliases in (manifest.get("core_names") or {}).items():
names[profile] = profile
for alias in aliases:
names.setdefault(alias, profile)
return names
def _print_emulators(plat: str, manifest: dict, files: list[dict], omitted: list[dict]) -> None:
"""What --emulator accepts for a platform, one name per line."""
names = emulator_names(manifest, files, omitted)
if not names:
print(f" {plat} records no emulators")
return
width = min(max(len(name) for name in names), 40)
for name in sorted(names):
profile = names[name]
print(f" {name:<{width}} {'' if profile == name else 'same as ' + profile}".rstrip())
def _older_list_advice() -> str:
"""How to reach a file list that records what an option needs.
@@ -3952,11 +3980,7 @@ def _narrow_to_emulators(
"which systems, which --emulator needs.", file=sys.stderr)
print(_older_list_advice(), file=sys.stderr)
sys.exit(1)
known = sorted({
name
for profile, aliases in (manifest.get("core_names") or {}).items()
for name in [profile, *aliases]
} | set(available_choices(files, omitted)["cores"]))
known = sorted(emulator_names(manifest, files, omitted))
try:
chosen = resolve_choices(names, known, "emulator")
except ValueError as exc:
@@ -4093,7 +4117,7 @@ def main() -> None:
action="append",
metavar="NAME",
help="install what these emulators read on the platform, as a pack "
"built for them carries (names: --list-cores)",
"built for them carries (names: --list-emulators)",
)
parser.add_argument(
"--installed-cores",
@@ -4138,6 +4162,11 @@ def main() -> None:
action="store_true",
help="list the cores whose extra files a platform installs and exit",
)
parser.add_argument(
"--list-emulators",
action="store_true",
help="list every name --emulator accepts for a platform and exit",
)
parser.add_argument(
"--no-input",
action="store_true",
@@ -4183,7 +4212,7 @@ def main() -> None:
parser.error(f"{option} needs at least one name")
if args.quiet and (
args.check or args.dry_run or args.list_platforms or args.list_targets
or args.list_systems or args.list_cores
or args.list_systems or args.list_cores or args.list_emulators
):
parser.error("--quiet applies to an install, not to --check, --dry-run or a listing")
if args.every_board and args.target:
@@ -4271,7 +4300,8 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
"""One run of the installer, once the options are read."""
term = Terminal(real_stdout, quiet=args.quiet)
interactive = sys.stdin.isatty() and not args.no_input and not args.quiet
listing = args.list_targets or args.list_systems or args.list_cores
listing = (args.list_targets or args.list_systems or args.list_cores
or args.list_emulators)
print(f"{term.bold('RetroBIOS')} {term.dim(installer_id())}\n")
os_type = detect_os()
@@ -4500,6 +4530,10 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
)
print(f" Narrowed {before} -> {len(files)} files to the installed cores")
if args.list_emulators:
_print_emulators(plat_name, manifest, files, omitted_files)
continue
if args.emulator:
files, omitted_files = _narrow_to_emulators(
args.emulator, manifest, plat_name, files, omitted_files
+1 -1
View File
@@ -18,7 +18,7 @@ esac
TEMP_INSTALLER=""
TEMP_DIRECTORY=""
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
DEFAULT_INSTALL_SHA256="df46e48289c89c5c542095b01c16208ef54696df472685c99327e5022fcd7ab8"
DEFAULT_INSTALL_SHA256="2409cff28ad4eed1546161aa5abd6808fcd6a010bda7943eb1076f4ab0559c9f"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+36
View File
@@ -3711,5 +3711,41 @@ class ACompletePartialIsHandedOffOnWindows(unittest.TestCase):
self.assertTrue(install._write_refused(PermissionError(13, "denied")))
class TheEmulatorNamesHaveTheirOwnListing(unittest.TestCase, _LoopbackRun):
"""--emulator nope said "--list-cores lists all 441" and --list-cores
printed 189: the buildbot names --emulator accepts were listed nowhere."""
def setUp(self):
self.env = self.serve(self)
def test_the_hint_counts_what_the_listing_prints(self):
proc = _install_run(self.env, "--platform", "retroarch", "--no-input",
"--emulator", "nope")
self.assertEqual(proc.returncode, 1, proc.stdout)
found = re.search(r"--list-emulators lists all (\d+)\.", proc.stderr)
self.assertIsNotNone(found, proc.stderr)
listing = _install_run(self.env, "--platform", "retroarch", "--no-input",
"--list-emulators")
self.assertEqual(listing.returncode, 0, listing.stderr)
names = [re.split(r"\s{2,}", line.strip())[0]
for line in listing.stdout.splitlines() if line.startswith(" ")]
manifest = json.loads((REPO_ROOT / "install" / "retroarch.json").read_text())
accepted = install.emulator_names(
manifest, manifest["files"], manifest.get("omitted_files", []))
self.assertEqual(int(found.group(1)), len(accepted))
self.assertTrue(set(accepted) <= set(names), set(accepted) - set(names))
self.assertIn("mednafen_psx", accepted)
self.assertRegex(listing.stdout, r"mednafen_psx\s+same as beetle_psx")
def test_every_listed_name_is_accepted(self):
manifest = {"core_names": {"beetle_psx": ["mednafen_psx"]}}
files = [{"dest": "a", "cores": ["pcsx_rearmed"]}]
names = install.emulator_names(manifest, files, [])
self.assertEqual(names, {"beetle_psx": "beetle_psx", "mednafen_psx": "beetle_psx",
"pcsx_rearmed": "pcsx_rearmed"})
self.assertEqual(install.resolve_choices(["MEDNAFEN_PSX"], sorted(names), "emulator"),
["mednafen_psx"])
if __name__ == "__main__":
unittest.main()
+2 -1
View File
@@ -230,10 +230,11 @@ what the options name, everything by default.
| `--cores-dir PATH` | The folder RetroArch loads its cores from, for `--installed-cores` when it is not found. Implies `--installed-cores` |
| `--system NAMES` | Install only these systems. Comma-separated or repeated. `--list-systems` prints the names, `other` included |
| `--core NAMES` | Keep the platform's own list and only the extra files these cores load. `--list-cores` prints the names |
| `--emulator NAMES` | Install what these emulators read on the detected platform, and nothing else: their own files and the platform's files of the systems they run. Each emulator page on the site gives this command. `--list-cores` prints the names |
| `--emulator NAMES` | Install what these emulators read on the detected platform, and nothing else: their own files and the platform's files of the systems they run. Each emulator page on the site gives this command. `--list-emulators` prints the names, buildbot names such as `mednafen_psx` included |
| `--region LIST` | Preferred regions, best first (`us,eu,jp`): keep one BIOS per region where a system has several. Accepts the names and aliases of `generate_pack.py --region` |
| `--list-systems` | Print a platform's systems with the files and size each holds |
| `--list-cores` | Print the cores whose extra files a platform installs |
| `--list-emulators` | Print every name `--emulator` accepts, each buildbot name beside the emulator it stands for |
| `--no-input` | Never ask a question, even at a terminal |
| `--check` | Report and exit without writing |
| `--dry-run` | Show what would be downloaded, by system and with sizes, without writing anything |