autocompile: name the resolved interpreter at configure, warn on Cygwin builds

Root-caused on Tomba 1: overlay_autocompile_cmd's bare 'python' resolved
through PATH to the devkitPro MSYS2/Cygwin python (usr/bin precedes the
Windows install), which segfaults (exit 0x0B00, signal 11) under the
kill-on-close job spawn BEFORE writing a byte — so every compile failed
with '(no output captured)' and nothing pointed at the cause; the title
silently ran interpreted.

At configure time, resolve the command's first token exactly as
cmd.exe /C will (SearchPathA with .exe), print the binding once, and
warn loudly when msys-2.0.dll/cygwin1.dll sits beside the resolved
interpreter, recommending the Windows launcher form 'py -3 ...'.
Also fail loudly up front when the token doesn't resolve at all.

Doc'd in ASYNC_OVERLAY_COMPILE.md. Title game.toml fixes (python ->
py -3) ship separately in each game repo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-08-04 14:49:29 -07:00
parent e629d622fa
commit f3d60331a7
2 changed files with 70 additions and 1 deletions
+6 -1
View File
@@ -13,8 +13,13 @@ What this session ADDED to make it work under CPS + match the user's gcc-only pr
2. `overlay_loader.c overlay_loader_apply_live_policy` — when the active backend is gcc, sljit-live
is forced OFF (gcc>interp; toolchain-less stays sljit>interp) — "mutually exclusive" per user.
3. The dev/soak config (`tomba_soak_a.toml`, untracked): `overlay_cache=true` +
`overlay_autocompile_cmd = python ../psxrecomp/tools/compile_overlays.py … --cps`. The PRODUCTION
`overlay_autocompile_cmd = py -3 ../psxrecomp/tools/compile_overlays.py … --cps`. The PRODUCTION
`game.toml` overlay_autocompile_cmd needs `--cps` appended when CPS ships to master.
(Windows recipes must invoke the interpreter as `py -3`, never bare `python`: on machines where
an MSYS2/Cygwin `usr/bin` precedes the Windows Python on PATH, `python` binds to the Cygwin
build, which segfaults under the runtime's job-object spawn before writing any output — every
compile fails with "(no output captured)". The runtime prints the resolved interpreter at
startup and warns when it is a Cygwin binary.)
VALIDATED: autocapture fired (triggers≥1) → background `compile_overlays --cps` produced CPS
overlay DLLs (new CRCs, post-cache-clear) → loader loaded them (`loads`>0) → `dispatch_native`
+64
View File
@@ -560,6 +560,69 @@ static DWORD WINAPI watch_thread(LPVOID arg) {
}
#endif /* _WIN32 */
#ifdef _WIN32
/* Name the interpreter the spawn will actually run, once, at configure time.
* The command line's first token resolves through PATH exactly as cmd.exe /C
* will resolve it, and on machines where an MSYS2/Cygwin usr/bin precedes the
* Windows Python install, a bare `python` silently binds to the Cygwin build.
* That interpreter dies with SIGSEGV (exit 0x0B00) under this file's
* job-object spawn BEFORE writing a single byte, so the failure report shows
* "(no output captured)" and nothing points at the cause. Resolving and
* printing the binding up front turns that class of breakage into a one-line
* diagnosis; the msys-2.0.dll/cygwin1.dll sibling check calls it out loudly
* before the first compile ever runs. */
static void autocompile_report_interpreter(void) {
char tok[MAX_PATH];
size_t n = 0;
const char *p = s_cmd;
if (*p == '"') { /* quoted interpreter path */
p++;
while (*p && *p != '"' && n + 1 < sizeof(tok)) tok[n++] = *p++;
} else {
while (*p && *p != ' ' && n + 1 < sizeof(tok)) tok[n++] = *p++;
}
tok[n] = '\0';
if (!tok[0]) return;
char resolved[MAX_PATH];
/* SearchPathA with .exe mirrors cmd.exe's lookup for extension-less
* tokens; a token that already has a path or extension resolves as-is. */
DWORD r = SearchPathA(NULL, tok, ".exe", sizeof(resolved), resolved, NULL);
if (r == 0 || r >= sizeof(resolved)) {
fprintf(stdout,
"psxrecomp: overlay autocompile interpreter '%s' does not resolve "
"on PATH — every compile run will fail.\n", tok);
fflush(stdout);
return;
}
fprintf(stdout, "psxrecomp: overlay autocompile interpreter: %s\n", resolved);
char *slash = strrchr(resolved, '\\');
if (!slash) slash = strrchr(resolved, '/');
if (slash) {
static const char *posix_dlls[] = { "msys-2.0.dll", "cygwin1.dll" };
for (size_t k = 0; k < sizeof(posix_dlls) / sizeof(posix_dlls[0]); k++) {
char cand[MAX_PATH + 16];
snprintf(cand, sizeof(cand), "%.*s\\%s",
(int)(slash - resolved), resolved, posix_dlls[k]);
FILE *f = fopen(cand, "rb");
if (f) {
fclose(f);
fprintf(stdout,
"psxrecomp: WARNING: that interpreter is an MSYS2/Cygwin "
"build (%s beside it).\n"
" Cygwin-runtime processes crash under the autocompile "
"job spawn before producing output,\n"
" so every overlay compile will fail with no diagnostics. "
"Use the Windows Python launcher\n"
" instead: overlay_autocompile_cmd = \"py -3 ...\" in "
"game.toml.\n", posix_dlls[k]);
fflush(stdout);
break;
}
}
}
}
#endif
void autocompile_configure(const char *cmd, const char *cwd) {
snprintf(s_cmd, sizeof(s_cmd), "%s", cmd ? cmd : "");
snprintf(s_cwd, sizeof(s_cwd), "%s", cwd ? cwd : "");
@@ -569,6 +632,7 @@ void autocompile_configure(const char *cmd, const char *cwd) {
InitializeConditionVariable(&s_publish_cv);
s_out_lock_init = 1;
}
if (s_cmd[0]) autocompile_report_interpreter();
#endif
}