Подготовить автономную сборку и запуск перед разделением проектов

This commit is contained in:
Александр Петров
2026-09-15 23:04:04 +03:00
parent 5323b168a7
commit 0e74aaa7ee
116 changed files with 6908 additions and 333 deletions
+75 -60
View File
@@ -20,7 +20,7 @@ mame_interactive.py — автотест .exe в MAME без участия че
автоповтор (typematic) и слипание scancode'ов.
- загрузка должна встать на приглашении C:\> (system.bat не должен
автозапускать Flex Navigator/приложение).
- перед запуском проверяем, что нет висящих копий MAME.
- тестовый носитель, конфигурация и состояние MAME изолированы от других сессий.
Использование:
python3 toolchain/mame_interactive.py [exe] [--data f ...] \
@@ -53,27 +53,16 @@ mame_interactive.py — автотест .exe в MAME без участия че
import argparse
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import uuid
from mame_profile import MameProfile, add_arguments, from_arguments, write_keyboard_config
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MAME_DIR = os.path.join(PROJECT_ROOT, "mame", "v306")
IMG_DIR = os.path.join(MAME_DIR, "IMG")
MC_IMG = os.path.join(IMG_DIR, "mc.img")
SNAP_DIR = os.path.join(MAME_DIR, "snap_auto")
LUA_SCRIPT = os.path.join(MAME_DIR, "_interactive_gen.lua")
COMMON_ARGS = [
"-skip_gameinfo", "-video", "opengl", "-window", "-nofilter",
"-beta:wd179x:0", "35hd", "-beta:wd179x:1", "35hd",
"-flop1", MC_IMG, "-flop2", os.path.join(IMG_DIR, "dss171u.img"),
"-isa0", "zxbus_adapter", "-isa0:zxbus_adapter:card", "neogs",
"-hard1", os.path.join(IMG_DIR, "sp_hdd_sys.chd"),
"-hard2", os.path.join(IMG_DIR, "sp_hdd_media.chd"),
"-ata2:0", "cdrom", "-cdrom", os.path.join(IMG_DIR, "SprinterCD.iso"),
"-bios", "v3.06",
]
# --- Раскладка AT-клавиатуры :kbd:ms_naturl (tag, mask) ---
# Снято дампом ioport-полей в MAME (см. docs/mame-autotest.md).
@@ -207,21 +196,7 @@ emu.register_periodic(function()
end
end)
"""
with open(LUA_SCRIPT, "w") as f:
f.write(lua)
def check_no_stray_mame():
# Ловим только запущенный ЭМУЛЯТОР (mame/mame.arm/mame.exe c машиной
# sprinter), а не всё, где в командной строке встречается «mame»:
# голый паттерн ложно срабатывал на параллельную сборку MAME из
# исходников (clang/make с путями .../src/mame/...).
out = subprocess.run(["pgrep", "-fl", r"mame[^ ]* sprinter( |$)"],
capture_output=True, text=True).stdout
if out.strip():
print("ОШИБКА: есть запущенные копии MAME — закройте их:")
print(out)
sys.exit(1)
return lua
def parse_step(s):
@@ -234,6 +209,7 @@ def parse_step(s):
def main():
ap = argparse.ArgumentParser()
add_arguments(ap)
ap.add_argument("exe", nargs="?", help=".exe на дискету A: (авто-запуск)")
ap.add_argument("--data", nargs="*", default=[], help="доп. файлы на дискету")
ap.add_argument("--launch-at", type=float, default=8.0,
@@ -242,9 +218,15 @@ def main():
help="'T:TEXT' — в момент T сек напечатать TEXT (можно много)")
ap.add_argument("--snap", default=None, help="секунды для скриншотов, через запятую")
ap.add_argument("--timeout", type=float, default=None, help="сек эмуляции до выхода")
ap.add_argument("--snapshot-dir", help="каталог кадров, по умолчанию build/mame-autotest")
args = ap.parse_args()
check_no_stray_mame()
profile = from_arguments(args)
if profile.binary is None and (Path(PROJECT_ROOT)/"mame/v306/mame.arm").is_file():
profile = MameProfile.resolve({"MAME_HOME":str(Path(PROJECT_ROOT)/"mame/v306")})
try:
profile.validate(dss=True)
except ValueError as error:
ap.error(str(error))
# Собираем шаги ввода: авто-запуск exe (печать пути) + пользовательские.
steps = list(args.step)
@@ -272,7 +254,7 @@ def main():
snap_times = [last_ev_t + pad, last_ev_t + pad + 2.0]
timeout = args.timeout if args.timeout is not None else (max(snap_times) + 1.5)
gen_lua(events, snap_times, timeout)
lua_source = gen_lua(events, snap_times, timeout)
# Дискета БЕЗ авто-запускаемого bat → приглашение C:\>.
disk_files = []
@@ -280,34 +262,67 @@ def main():
disk_files.append(exe_path)
disk_files += [os.path.abspath(p) for p in args.data]
if os.path.exists(MC_IMG):
shutil.copy(MC_IMG, MC_IMG + ".bak")
make_disk_cmd = [sys.executable, os.path.join(MAME_DIR, "make_disk.py"), MC_IMG]
subprocess.run(make_disk_cmd + disk_files, check=True, cwd=MAME_DIR)
snapshot_base = Path(args.snapshot_dir or Path.cwd()/"build/mame-autotest")
snapshot_root = snapshot_base.resolve()/uuid.uuid4().hex[:10]
snapshot_root.mkdir(parents=True)
with tempfile.TemporaryDirectory(prefix="sprinter-mame-autotest-") as directory:
state = Path(directory)
for name in ("cfg", "nvram", "diff"):
(state/name).mkdir()
write_keyboard_config(state/"cfg")
disk = state/"test.img"
subprocess.run([sys.executable, str(Path(PROJECT_ROOT)/"toolchain/make_disk.py"),
str(disk), *disk_files], check=True)
lua_script = state/"interactive.lua"
lua_script.write_text(lua_source, encoding="utf-8")
cmd = [str(profile.binary), "sprinter", "-noreadconfig",
"-rompath", str(profile.rompath), "-bios", profile.bios,
"-kbd", "ms_naturl,bios=sp2k", "-skip_gameinfo",
"-video", "soft", "-window", "-beta:wd179x:0", "35hd",
"-beta:wd179x:1", "35hd", "-flop1", str(disk),
"-flop2", str(profile.dss_image),
"-hard1", str(profile.system_hdd_image),
"-isa0", "zxbus_adapter", "-isa0:zxbus_adapter:card", "neogs",
"-snapshot_directory", str(snapshot_root),
"-autoboot_script", str(lua_script)]
for name in ("cfg", "nvram", "diff"):
cmd += ["-"+name+"_directory", str(state/name)]
if profile.home:
media = profile.home/"IMG/sp_hdd_media.chd"
optical = profile.home/"IMG/SprinterCD.iso"
if media.is_file():
test_media = state/"media.chd"
shutil.copyfile(media, test_media)
cmd += ["-hard2", str(test_media)]
if optical.is_file():
cmd += ["-ata2:0", "cdrom", "-cdrom", str(optical)]
print("Ввод по шагам:", [(round(t, 2), repr(txt)) for t, txt in steps])
print("Скриншоты (сек):", snap_times, " таймаут:", timeout)
try:
result = subprocess.run(cmd, cwd=state, capture_output=True,
text=True, timeout=timeout+8)
except subprocess.TimeoutExpired as error:
print("MAME: истёк лимит времени", file=sys.stderr)
output = error.stdout or ""
stderr = error.stderr or ""
if isinstance(output, bytes):
output = output.decode(errors="replace")
if isinstance(stderr, bytes):
stderr = stderr.decode(errors="replace")
result = subprocess.CompletedProcess(cmd, 124, output, stderr)
for line in result.stdout.splitlines():
if "[interactive]" in line:
print(line)
if result.returncode not in (0, 124):
print("STDERR:", result.stderr[-2000:])
snap_sub = os.path.join(SNAP_DIR, "sprinter")
if os.path.isdir(snap_sub):
shutil.rmtree(snap_sub)
cmd = ["timeout", str(int(timeout) + 8), "./mame.arm", "sprinter"] + COMMON_ARGS + [
"-snapshot_directory", SNAP_DIR,
"-autoboot_script", LUA_SCRIPT,
]
print("Ввод по шагам:", [(round(t, 2), repr(txt)) for t, txt in steps])
print("Скриншоты (сек):", snap_times, " таймаут:", timeout)
result = subprocess.run(cmd, cwd=MAME_DIR, capture_output=True, text=True)
for line in result.stdout.splitlines():
if "[interactive]" in line:
print(line)
if result.returncode not in (0, 124):
print("STDERR:", result.stderr[-2000:])
if os.path.isdir(snap_sub):
snap_sub = snapshot_root/"sprinter"
if snap_sub.is_dir():
print("Скриншоты:")
for s in sorted(os.listdir(snap_sub)):
print(" ", os.path.join(snap_sub, s))
for shot in sorted(snap_sub.iterdir()):
print(" ", shot)
else:
print("Скриншотов не создано.")
print("Скриншотов не создано. Каталог:", snapshot_root)
if __name__ == "__main__":