mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
f180a32ec1
Decision (Alex, 2026-09-30): the recompiled BIOS carries the same risk as
the recompiled game, which titles already commit, so the retail backend is
compiled in rather than built on the player's machine. The retail image is
still never shipped: the runtime needs its data at run time and accepts only
the exact image the backend was built from, so the player supplies their
own dump as before.
- generated/{OpenBIOS,SCPH1001}_{full,dispatch}.c and their .emitter.sha
stamps are tracked (.gitignore un-ignores exactly those); regenerated with
the current emitter.
- bios_emitter_fingerprint.sh no longer hashes the ROM image: the profile's
SHA-256 pin stands for it and the emitter refuses any other image, and CI
has no dump, so hashing it made every CI configure read "stale".
- runtime.cmake: the staleness check covers every LINKED stem and
-DPSXRECOMP_BIOS_STALE_FATAL=ON makes a stale stamp fail configure.
- game-release.yml: no CI-side OpenBIOS emission (generate_openbios.sh is
gone); configure with BIOS_STALE_FATAL and assert both backends linked.
check_generated.sh asserts the submodule carries both backends tracked
and no dump is tracked anywhere.
- The player-side module build stays for a known image a build does not
link (SCPH-5552).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
140 lines
5.9 KiB
Bash
Executable File
140 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Gate a bundled release on the committed game C being present and clean.
|
|
#
|
|
# Releases ship a compiled game: CI builds from the generated/ tree the title
|
|
# COMMITS, so a checkout missing it (or carrying the wrong marker name) must
|
|
# fail here, at the top of the job, not twenty minutes later at link -- and
|
|
# never on a player's machine, which is where the old setup-host model put
|
|
# every failure of this kind.
|
|
#
|
|
# Asserts, from the game repo root:
|
|
# 1. generated/<boot>_dispatch.c exists, where <boot> is the boot-EXE name
|
|
# game.toml declares (the same name check_boot_exe.sh cross-checks), and
|
|
# at least one generated/<boot>_full*.c shard sits beside it.
|
|
# 2. Those files are tracked by git. An untracked generated/ builds fine on
|
|
# the developer's machine and is absent from every CI checkout, which is
|
|
# the exact gap between "works for me" and a red release.
|
|
# 3. The framework's BIOS backends are present and tracked in the submodule
|
|
# (psxrecomp/generated/{OpenBIOS,SCPH1001}_{full,dispatch}.c with their
|
|
# .emitter.sha stamps). Decision 2026-09-30: the recompiled BIOS is
|
|
# committed and linked like the recompiled game; the player still
|
|
# supplies the retail image at run time. runtime.cmake's fingerprint
|
|
# check (PSXRECOMP_BIOS_STALE_FATAL in CI) refuses a stale stamp.
|
|
# 4. No BIOS dump is tracked (SCPH*.BIN / *.bin under bios/) in the title or
|
|
# the submodule.
|
|
#
|
|
# Usage: check_generated.sh [--root DIR] (default: cwd)
|
|
# Runs on bash 3.2+ (macOS runners).
|
|
set -euo pipefail
|
|
|
|
ROOT="${PWD}"
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--root) ROOT="${2:?}"; shift 2 ;;
|
|
-h|--help)
|
|
sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "error: unknown arg: $1" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
ROOT="$(cd "${ROOT}" && pwd)"
|
|
|
|
toml_get() { # <section> <key> -- first quoted value of key inside [section]
|
|
awk -v sec="$1" -v key="$2" '
|
|
/^[[:space:]]*\[/ { in_sec = ($0 ~ ("^[[:space:]]*\\[" sec "\\]")) }
|
|
in_sec && $0 ~ ("^[[:space:]]*" key "[[:space:]]*=") {
|
|
if (match($0, /"[^"]*"/)) { print substr($0, RSTART + 1, RLENGTH - 2) }
|
|
exit
|
|
}' "${ROOT}/game.toml"
|
|
}
|
|
|
|
fail() { echo "error: $*" >&2; exit 1; }
|
|
|
|
[[ -f "${ROOT}/game.toml" ]] || fail "${ROOT}/game.toml missing -- not a game repo root"
|
|
|
|
boot="$(toml_get prepare_disc boot_exe || true)"
|
|
if [[ -z "${boot}" ]]; then
|
|
exe="$(toml_get game exe || true)"
|
|
boot="${exe##*/}"
|
|
fi
|
|
if [[ -z "${boot}" && -f "${ROOT}/CMakeLists.txt" ]]; then
|
|
boot="$(sed -n 's/.*GEN_MARKER[[:space:]]*"generated\/\(.*\)_dispatch\.c".*/\1/p' \
|
|
"${ROOT}/CMakeLists.txt" | head -n1)"
|
|
fi
|
|
[[ -n "${boot}" ]] || fail "cannot determine the boot-EXE name (game.toml [prepare_disc] boot_exe / [game] exe, or CMakeLists GEN_MARKER)"
|
|
|
|
gen="${ROOT}/generated"
|
|
dispatch="${gen}/${boot}_dispatch.c"
|
|
|
|
if [[ ! -f "${dispatch}" ]]; then
|
|
echo "error: ${dispatch#"${ROOT}"/} is missing." >&2
|
|
echo " Bundled releases build from COMMITTED game C. Generate it once from a" >&2
|
|
echo " legal disc and commit generated/:" >&2
|
|
echo " python3 psxrecomp/psxrecomp_cli.py generate --config game.toml \\" >&2
|
|
echo " --project-root . --disc disc/<game>.cue" >&2
|
|
echo " git add generated && git commit -m 'Add generated game C'" >&2
|
|
if grep -qE '^/?generated/?[[:space:]]*$' "${ROOT}/.gitignore" 2>/dev/null; then
|
|
echo " .gitignore still ignores generated/ (the old setup-host rule) -- remove" >&2
|
|
echo " that line; see psxrecomp/docs/ci/templates/game.gitignore." >&2
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
shards=0
|
|
for f in "${gen}/${boot}_full"*.c; do
|
|
[[ -f "${f}" ]] && shards=$((shards + 1))
|
|
done
|
|
[[ "${shards}" -gt 0 ]] || fail "no generated/${boot}_full*.c beside ${boot}_dispatch.c -- regenerate"
|
|
|
|
# 2. Tracked, not merely present.
|
|
if git -C "${ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
untracked="$(git -C "${ROOT}" ls-files --others --exclude-standard -- "generated/${boot}_dispatch.c" "generated/${boot}_full"*.c 2>/dev/null || true)"
|
|
ignored="$(git -C "${ROOT}" ls-files --others --ignored --exclude-standard -- "generated/${boot}_dispatch.c" "generated/${boot}_full"*.c 2>/dev/null || true)"
|
|
if [[ -n "${untracked}${ignored}" ]]; then
|
|
echo "error: generated game C is present but NOT tracked by git:" >&2
|
|
printf '%s\n' ${untracked} ${ignored} | sed 's/^/ /' >&2
|
|
echo " A release builds from the checkout, so these files never reach CI." >&2
|
|
echo " Remove any /generated/ rule from .gitignore, then: git add generated" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# 3. The framework's committed BIOS backends.
|
|
fw="${ROOT}/psxrecomp"
|
|
if [[ -d "${fw}" ]]; then
|
|
for stem in OpenBIOS SCPH1001; do
|
|
for f in "generated/${stem}_full.c" "generated/${stem}_dispatch.c" "generated/${stem}.emitter.sha"; do
|
|
if [[ ! -f "${fw}/${f}" ]]; then
|
|
echo "error: psxrecomp/${f} is missing -- the framework commits its BIOS backends;" >&2
|
|
echo " this submodule pin predates that, or the checkout is not --recurse-submodules." >&2
|
|
exit 1
|
|
fi
|
|
if git -C "${fw}" rev-parse --is-inside-work-tree >/dev/null 2>&1 &&
|
|
! git -C "${fw}" ls-files --error-unmatch -- "${f}" >/dev/null 2>&1; then
|
|
echo "error: psxrecomp/${f} exists but is not tracked in the framework submodule." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
done
|
|
fi
|
|
|
|
# 4. No BIOS dump tracked, in the title or the submodule.
|
|
for repo in "${ROOT}" "${fw}"; do
|
|
[[ -d "${repo}" ]] || continue
|
|
if git -C "${repo}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
dumps="$(git -C "${repo}" ls-files -- 'bios/*' '*.BIN' 'SCPH*' 2>/dev/null \
|
|
| grep -Ei '\.(bin|rom)$' | grep -v -i 'openbios' || true)"
|
|
if [[ -n "${dumps}" ]]; then
|
|
echo "error: BIOS image(s) tracked in ${repo#"${ROOT}"}:" >&2
|
|
printf '%s\n' "${dumps}" | sed 's/^/ /' >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
done
|
|
|
|
echo "generated C ok: generated/${boot}_dispatch.c + ${shards} full shard(s) tracked; framework BIOS backends present; no BIOS dump tracked"
|