Files
libretro/scripts/generate_truth.py
T
Abdessamad Derraz 4d3f589ef4 refactor: drop the archived platform status
RetroPie was the only platform with status: archived, which kept it out
of verify --all, slots, the pipeline and list_platforms unless
--include-archived was typed. release_pack already decides which packs a
release publishes and the installer serves every platform, so the status
set nothing apart. The field, its schema entry and the flag are gone, and
RetroPie is covered by every full run like any other platform.
2026-11-06 11:52:34 +01:00

171 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""Generate ground-truth YAML files per platform from emulator profiles.
Usage:
python scripts/generate_truth.py --platform retroarch --output-dir dist/truth
python scripts/generate_truth.py --all --output-dir dist/truth
"""
from __future__ import annotations
import argparse
import os
import sys
sys.path.insert(0, os.path.dirname(__file__))
from common import (
build_target_cores_cache,
platform_arg,
list_registered_platforms,
load_data_dir_registry,
load_database,
load_emulator_profiles,
load_platform_config,
require_directory,
require_platform,
require_yaml,
truth_target_dir,
write_text_atomic,
yaml_load,
)
from truth import generate_platform_truth
yaml = require_yaml()
DEFAULT_OUTPUT_DIR = "dist/truth"
DEFAULT_PLATFORMS_DIR = "platforms"
DEFAULT_EMULATORS_DIR = "emulators"
DEFAULT_DB_FILE = "database.json"
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
allow_abbrev=False,
description="Generate ground-truth YAML from emulator profiles",
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--all", action="store_true", help="all registered platforms")
group.add_argument("--platform", "-p", type=platform_arg, help="single platform name")
parser.add_argument(
"--output-dir",
default=DEFAULT_OUTPUT_DIR,
help="output directory",
)
parser.add_argument(
"--target",
"-t",
default=None,
help="hardware target filter",
)
parser.add_argument(
"--platforms-dir",
default=DEFAULT_PLATFORMS_DIR,
help="platform YAML directory (default: %(default)s)",
)
parser.add_argument(
"--emulators-dir",
default=DEFAULT_EMULATORS_DIR,
help="emulator profile directory (default: %(default)s)",
)
parser.add_argument("--db", default=DEFAULT_DB_FILE, help="database.json path")
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> None:
args = parse_args(argv)
require_directory(args.platforms_dir, "--platforms-dir")
registry_path = os.path.join(args.platforms_dir, "_registry.yml")
with open(registry_path) as f:
registry = (yaml_load(f) or {}).get("platforms", {})
profiles = load_emulator_profiles(args.emulators_dir)
data_registry = load_data_dir_registry(args.platforms_dir)
# Load database (optional)
# The default may be absent, but a --db that was named must exist.
db: dict | None = None
if args.db != DEFAULT_DB_FILE or os.path.exists(args.db):
db = load_database(args.db)
if args.all:
platforms = list_registered_platforms(
args.platforms_dir,
)
else:
require_platform(args.platform, args.platforms_dir)
platforms = [args.platform]
# A named platform without that target is refused, as verify and
# generate_pack refuse it: skipped, it printed Done. and exited 0 with
# no model written. --all leaves such platforms out and says so.
target_cores_cache: dict[str, set[str] | None] = {}
if args.target:
try:
target_cores_cache, platforms = build_target_cores_cache(
platforms, args.target, args.platforms_dir, is_all=args.all
)
except (FileNotFoundError, ValueError) as exc:
print(f"ERROR: {exc}", file=sys.stderr)
sys.exit(1)
# A target-narrowed model is a different artifact: written under the
# platform's own name it overwrote the full one, and diff_truth then
# compared a narrowed model against the whole scrape. Its directory is
# the target as this platform files it, as the packs are named: Lakka's
# switch is its own image, RetroArch's is nintendo-switch.
for name in platforms:
output_dir = args.output_dir
if args.target:
output_dir = truth_target_dir(
output_dir, [name], args.target, args.platforms_dir
)
os.makedirs(output_dir, exist_ok=True)
target_cores = target_cores_cache.get(name)
# Load platform config (with inheritance) and registry entry
try:
config = load_platform_config(name, args.platforms_dir)
except FileNotFoundError:
print(f" {name}: no platform config, skipped")
continue
registry_entry = registry.get(name, {})
result = generate_platform_truth(
name,
config,
registry_entry,
profiles,
db=db,
target_cores=target_cores,
data_registry=data_registry,
)
out_path = os.path.join(output_dir, f"{name}.yml")
# Whole or not at all: export_native and diff_truth read this file,
# and a run stopped midway left a truncated model behind.
write_text_atomic(
out_path,
yaml.dump(
result,
default_flow_style=False,
sort_keys=False,
allow_unicode=True,
),
)
n_systems = len(result.get("systems", {}))
n_files = sum(
len(sys_data.get("files", {}))
for sys_data in result.get("systems", {}).values()
)
print(f" {name}: {n_systems} systems, {n_files} files -> {out_path}")
print("Done.")
if __name__ == "__main__":
from common import run_cli
run_cli(main)