#!/usr/bin/env python3 """ record_golden.py — снять эталонные трассы для t_phys. Зачем скрипт, а не один прогон. Отчёт tcheck ограничен 768 байтами лога, а трасс на все сценарии сильно больше. Поэтому набор умеет снимать РОВНО ОДИН сценарий за прогон (-DSC_RECORD=), а перебор делает этот скрипт: собирает набор N раз, каждый раз забирает из отчёта имя сценария и его трассу и пишет golden/<имя>.h. Эталон фиксирует ТЕКУЩЕЕ поведение. Это осознанно: набор страхует поведение-сохраняющие правки (перенос физики Kid -> Char), а не доказывает правильность. Правильность — сверка с SDLPoP. Поэтому переснимать эталоны можно только вместе с внятным объяснением, что и почему поменялось. Использование: python3 record_golden.py # снять все сценарии python3 record_golden.py 7 9 # только эти номера """ import re import subprocess import sys from pathlib import Path HERE = Path(__file__).resolve().parent GOLDEN = HERE / "golden" # Число сценариев = число TC_RUN в t_phys.c: список ведётся там, здесь его # дублировать незачем. NSCEN = len(re.findall(r"^\s*TC_RUN\(", (HERE / "t_phys.c").read_text(), re.M)) def record(n: int) -> str: # make не видит смены -DSC_RECORD (в зависимостях только файлы), поэтому # объектник набора сносим руками — иначе следующий прогон молча снимет # трассу предыдущего сценария под новым именем. for f in ("phys.rel", "phys.ihx"): (HERE / "build" / f).unlink(missing_ok=True) subprocess.run(["make", "-s", "build/phys.ihx", f"EXTRA_CFLAGS=-DSC_RECORD={n}"], cwd=HERE, check=True) p = subprocess.run(["make", "-s", "run-phys", f"EXTRA_CFLAGS=-DSC_RECORD={n}"], cwd=HERE, capture_output=True, text=True) out = p.stdout m = re.search(r"^\s*### (\S+)\s*$", out, re.M) if not m: raise SystemExit(f"сценарий {n}: в отчёте нет строки '### имя'\n{out}") name = m.group(1) rows = re.findall(r"^\s*([0-9A-F]{18})\s*$", out, re.M) if not rows: raise SystemExit(f"сценарий {n} ({name}): трасса пустая\n{out}") lines = [f"/* Эталон трассы: {name}. Снят record_golden.py — не править руками.", " * Поля кадра: frame, x, y, dir, col, row, action, seq_lo, seq_hi. */"] for r in rows: lines.append(" " + ", ".join(f"0x{r[i:i+2]}" for i in range(0, 18, 2)) + ",") GOLDEN.mkdir(exist_ok=True) (GOLDEN / f"{name}.h").write_text("\n".join(lines) + "\n") return f"{name}: {len(rows)} кадров" def main() -> int: nums = [int(a) for a in sys.argv[1:]] or list(range(1, NSCEN + 1)) for n in nums: print(f"[{n}/{NSCEN}] " + record(n)) # Итоговая пересборка в обычном режиме, чтобы каталог не остался с # объектниками, собранными под SC_RECORD. subprocess.run(["make", "-s", "clean"], cwd=HERE, check=True) print("эталоны сняты; проверить: make -C", HERE.name, "t_phys") return 0 if __name__ == "__main__": sys.exit(main())