GameVersion.cpp becomes a configure_file template, so the version strings are a
build input rather than something a CI step seds into the working tree mid-run.
That is what lets a crash report be matched to its exact build's PDB for offline
symbolization.
The two strings get distinct jobs. czVersionString is the machine-readable build
identity: the bare short commit SHA and nothing else. A `git describe` string
cannot serve here -- czVersionString is 16 bytes because it is stored in the
savegame header as SAVED_GAME_HEADER::zGameVersionNumber and strcmp'd on load,
and describe puts the SHA last, so truncation ate exactly the discriminating part
and could make two different commits compare equal. CI passes 9 hex; anything
that would not fit is now a configure error rather than a silent clip.
zBuildInformation is the display string, untruncated, and it is no longer printed
beside czVersionString: that drops the duplication in the version line ("JA2 1.13
V4-555-G6A941C0 2026-07-25 V4-555-G6A941C06") and stops showing a token that is
now machine-only. CI's copy leads with GAME_VERSION, because the version line is
now the only place a player reads which release they are running.
Finish what the sed step's TODO asked for: the workflow pins GAME_BUILD_INFORMATION
and GIT_SHA once, next to the gamedir SHAs it already pins, and hands them to CMake
with -D. Every parallel build job therefore stamps one agreed identity, and the
compile checkout needs no git history at all.
A build nobody identifies is a local build and says so -- czVersionString reads
"local" -- rather than deriving a SHA from HEAD. A derived SHA could not stay
true: nothing re-runs configure once HEAD moves, so an incremental build would
keep whatever it was configured with, and keeping it honest would mean
reconfiguring on every commit. It would also buy nothing, because only packaged
builds are archived with their PDBs; a local build's crash reports are symbolized
against the PDB sitting next to the exe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DEBUG.H once wrapped its declarations in extern "C", and DEBUG.cpp wrapped the
matching definitions to agree. At some point the header's pair was commented
out, both the opening brace and the closing one:
/*
#ifdef __cplusplus
extern "C" {
#endif
*/
That had to happen, because the block would otherwise have swallowed
<stdexcept>, <list> and namespace sgp further down the header. The wrapper in
DEBUG.cpp was left behind, so seven variables are declared with C++ linkage in
the header and defined with C linkage in the source. That is ill-formed. MSVC
takes the linkage of the first declaration it sees, which is the header's, so
the mangled names still agree and the tree still links; clang refuses.
The header is the only place these seven are declared, so removing the
wrapper leaves everything with the C++ linkage it already had. Nothing here
needs to be callable from C.
A second such wrapper remains further down the file, inside #ifdef SGP_DEBUG.
Nothing in the build defines SGP_DEBUG and nothing it contains is declared in
a header, so it is neither compiled nor mismatched, and it is left alone.
Verification:
ninja -C build parse # language-linkage class gone: 60 -> 55 sites
ninja -C build -k 0 # Release, four applications, green
ninja -C build-debug -k 0 # Debug, four applications, green
The seven diagnostics sit on five lines, three of them sharing a single
declaration, and the parse target counts one site per line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>