Harnesses could silently test a stale binary: `make` skips relinking when
file mtimes are second-identical, and nothing checked that the dlopened
core was the code under test. The Jaguar CD branch grew a guard for this;
port the branch-agnostic parts.
- scripts/build-id.sh (new): prints the short git rev plus "-dirty" when
tracked files are modified.
- scripts/gen-version-h.sh: take GIT_REV from build-id.sh so the string
stamped into the core matches what the guard compares against. Without
this the embedded rev lacks the dirty suffix and every dirty-tree run
would fail the guard spuriously.
- test/harness/harness.c: after resolving core symbols, print the core's
embedded version, and when VJ_EXPECT_BUILD is set refuse a core whose
version does not token-match it. Token-boundary matching so "9e39c1d"
does not accept a stale "9e39c1d-dirty" build.
- CLAUDE.md: document the guard; also note that test/baselines/ is not
committed and regression_test.sh bootstraps missing baselines as NEW
rather than failing.
Verified: embedded version reads "v2.3.1 9e39c1d-dirty"; a correct
VJ_EXPECT_BUILD loads normally, a wrong one and a clean-rev-vs-dirty
mismatch both refuse the load with a FATAL message and exit 1.
make TEST_EXPORTS=1 test passes (0 failures).
Test-only and docs; no emulation behavior changes.
Co-Authored-By: Claude Fable 5 <[email protected]>
Three hardening measures, motivated by a false PASS produced during the
lost-wakeup work when a harness silently loaded a stale core binary:
1. Build-identity guard. gen-version-h.sh now embeds "-dirty" in the
git rev for uncommitted trees (via new scripts/build-id.sh), the core
logs "[Virtual Jaguar] build: vX.Y.Z <rev>[-dirty]" unconditionally
at retro_init, and every harness (test_framework.h + harness.c)
prints the loaded core's version and hard-fails when VJ_EXPECT_BUILD
doesn't token-match it. `make test` and cd_boot_matrix.sh set
VJ_EXPECT_BUILD automatically; the matrix script also rebuilds when
the dylib doesn't embed the current id. Token-boundary matching
ensures a clean-tree id never accepts a stale "-dirty" binary.
2. SKIP is no longer counted/printed as PASS. RUN_TEST previously
printed "PASS" for any test body that returned without FAIL --
including ones that bailed out via SKIP_TEST (missing disc, missing
exports). That combination is exactly how the stale-binary false
PASS happened. Skipped tests now report only SKIP.
3. test/tools/cd_visual_verify.c (+ `make cd-visual`): automated visual
and audio verification for CD titles. Runs a disc N frames, prints a
per-second frame-motion / non-black / audio-RMS timeline, and dumps
periodic screenshots via a new harness video-pixel hook (harness.h
video_callback + system_dir/--system-dir for CD BIOS discovery).
Verified on BrainDead 13 (bios): the timeline and screenshots show
the BIOS cube animation, the FMV loading sequence, and the exact
frame window where the post-transfer-2 wall freezes on real game
artwork -- previously answerable only by booting a device.
make TEST_EXPORTS=1 test: 56103/56103 pass with the guard active.
Co-Authored-By: Claude Fable 5 <[email protected]>
Five real findings:
1. scripts/gen-version-h.sh: shebang said #!/usr/bin/env bash but the
script is pure POSIX sh. Switched to /usr/bin/env sh so it runs
in environments without bash. Updated the header comment to
describe the actual call sites (Makefile parse-time $(shell),
Android.mk, c89-lint, install-hooks pre-commit, msvc-check).
2. scripts/c89-lint.sh: now invokes the generator via \`sh\`, not
\`bash\` -- so the C89 lint hook works on POSIX-only environments.
3. jni/Android.mk: ndk-build doesn't go through the project Makefile,
so the parse-time version.h gen there didn't fire for local
Android builds (only c-cpp.yml's CI step had it). Added the
\`$(shell sh ... gen-version-h.sh ...)\` invocation to Android.mk.
Also dropped the now-redundant -DGIT_VERSION wiring (handled by
the generated header).
4. scripts/install-hooks.sh header comment: corrected dist/info/.info
path reference to "anything under dist/info/ or Makefile".
5. Makefile \`make benchmark\` target: -ldl is Linux-specific and was
hard-coded. macOS clang accepts it as a no-op but other linkers
may not. Now conditional via \`$(if $(filter Linux,$(uname)),-ldl)\`.
Local sanity: sh scripts/gen-version-h.sh runs clean, c89-lint passes,
make benchmark on Apple Silicon produces expected ~240 FPS without
spurious -ldl warnings.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Replaces the split GIT_VERSION/CORE_VERSION wiring (Makefile -DGIT_VERSION
+ libretro.c hardcoded "v2.2.0") with a generated src/core/version.h that
both sites read.
CORE_BASE_VERSION (Makefile) single source of truth
v
scripts/gen-version-h.sh portable composer
v
src/core/version.h (gitignored) #define CORE_VERSION ...
v
libretro.c #include "version.h"
Generation is content-aware (cmp + mv), so unchanged content leaves
mtime untouched and incremental builds stay incremental. Only changing
the version (or the git short rev) invalidates libretro.o.
CI: msvc-check compiles libretro.c directly without going through make,
so it runs scripts/gen-version-h.sh as a pre-step.
Also fixes version-bump.yml which was greping `library_version = "v..."`
in libretro.c -- a literal that no longer exists since the assignment
became `= CORE_VERSION`. The workflow now reads/writes the Makefile's
CORE_BASE_VERSION line.
Co-Authored-By: Claude Opus 4.7 <[email protected]>