diff --git a/psxrecomp_cli.py b/psxrecomp_cli.py index cc593f83..7f3d269b 100755 --- a/psxrecomp_cli.py +++ b/psxrecomp_cli.py @@ -1778,6 +1778,8 @@ def cmd_rebuild(args: argparse.Namespace, progress: ProgressReporter) -> int: if exe is None: progress.error(exe_err, code=EXIT_ERROR) return EXIT_ERROR + progress.phase("overlays", pct=0.93, message="Staging overlay toolchain beside the product...") + stage_overlay_toolchain_for_product(project_root, exe.parent, progress) prune_raw = (getattr(args, "prune_after", None) or "").strip() if prune_raw: @@ -1790,6 +1792,57 @@ def cmd_rebuild(args: argparse.Namespace, progress: ProgressReporter) -> int: return EXIT_OK +def stage_overlay_toolchain_for_product(project_root: Path, exe_dir: Path, progress) -> Optional[Path]: + """Stage overlay_toolchain/ beside a built product so the runtime's autocompile gate is + true for every player build (wave-5 F1), and name the compiler the wizard already has so + shards get an optimising compiler instead of tcc (F2). Best effort: a failure leaves the + product playable with overlays interpreted, and says so.""" + try: + fw = framework_root(project_root) + tools_dir = fw / "tools" + inc_dir = fw / "runtime" / "include" + game_emitter = find_psxrecomp_game(project_root) + if str(tools_dir) not in sys.path: + sys.path.insert(0, str(tools_dir)) + import release_stage # noqa: E402 + + dl_cache = project_root / ".cache" / "overlay-toolchain" + # Runtime DLLs only from the emitter's own directory, never from PATH: Git's + # mingw64 on PATH carries the MSVCRT runtime, which is the 0xC0000139 trap. + mingw_bin = game_emitter.parent if (game_emitter.parent / "libgcc_s_seh-1.dll").is_file() else None + tk = Path(release_stage.stage_toolchain( + str(exe_dir), str(game_emitter.parent), str(tools_dir), str(inc_dir), str(dl_cache), + mingw_bin=str(mingw_bin) if mingw_bin else None, log=progress.log)) + # The stale-recompiler check compares the emitter's codegen hash with the runtime's tag. + for cand in (exe_dir / "psxrecomp_codegen_include" / "overlay_codegen_hash.h", + inc_dir / "overlay_codegen_hash.h"): + if cand.is_file(): + shutil.copy2(cand, tk / "include" / "overlay_codegen_hash.h") + break + compiler = None + if sys.platform == "win32": + bin_dir = resolve_toolchain_bin(project_root) + if bin_dir and (bin_dir / "clang.exe").is_file(): + compiler = bin_dir / "clang.exe" + else: + for name in ("gcc", "cc", "clang"): + w = shutil.which(name) + if w: + compiler = Path(w) + break + if compiler: + (tk / "compiler.txt").write_text(str(compiler) + "\n", encoding="utf-8") + progress.log(f"overlay toolchain staged at {tk}; shard compiler: {compiler}") + else: + progress.log(f"overlay toolchain staged at {tk}; no optimising compiler found (tcc tier)") + return tk + except Exception as exc: # noqa: BLE001 + progress.log(f"WARNING: overlay toolchain staging failed; overlays will run interpreted: {exc}") + return None + + + + def cmd_pgo_train(args: argparse.Namespace, progress: ProgressReporter) -> int: args.force_pgo = True args.no_pgo = False diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index ca569218..b819287c 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -13530,6 +13530,20 @@ int main(int argc, char** argv) { const char *tk_tcc = "tcc"; #endif const bool tk_present = std::filesystem::exists(tk_py); + /* Wave-5 F2: the CLI names the compiler it already has (the wizard's pack on + * Windows, the native cc on POSIX) in overlay_toolchain/compiler.txt, so a player + * with nothing on PATH still gets optimised shards instead of tcc's. */ + std::string tk_compiler; + { + std::ifstream cf(tk_dir / "compiler.txt"); + std::string line; + if (cf.is_open() && std::getline(cf, line)) { + while (!line.empty() && (line.back() == '\r' || line.back() == '\n' || line.back() == ' ')) + line.pop_back(); + std::error_code cec; + if (!line.empty() && std::filesystem::exists(line, cec)) tk_compiler = line; + } + } auto build_toolchain_cmd = [&](const char *compiler) { auto cmd_quote = [](const std::string& s) { return std::string("\"") + s + "\""; @@ -13546,12 +13560,14 @@ int main(int argc, char** argv) { " --out-dir " + cmd_quote((tk_xd / "cache").string()) + (g_psx_cps_mode ? " --cps" : "") + " --compiler " + compiler; + if (std::string(compiler) == "gcc" && !tk_compiler.empty()) + c += " --gcc " + cmd_quote(tk_compiler); if (std::string(compiler) == "tcc") c += " --tcc " + cmd_quote((tk_dir / "tcc" / tk_tcc).string()); return c; }; int gcc_avail = (deferred_has_overlay_ac || tk_present) - && autocompile_toolchain_available(); + && (autocompile_toolchain_available() || !tk_compiler.empty()); OverlayBackend eff = overlay_backend_resolve(cfg_backend, gcc_avail); std::string built_tcc_cmd; std::string built_gcc_cmd; @@ -13579,8 +13595,9 @@ int main(int argc, char** argv) { built_gcc_cmd = build_toolchain_cmd("gcc"); ac_cmd = &built_gcc_cmd; std::fprintf(stdout, - "psxrecomp: gcc tier using bundled toolchain (%s) with gcc from PATH\n", - tk_dir.string().c_str()); + "psxrecomp: gcc tier using bundled toolchain (%s) with %s\n", + tk_dir.string().c_str(), + tk_compiler.empty() ? "gcc from PATH" : tk_compiler.c_str()); } } if (const char *e = std::getenv("PSX_OVERLAY_AUTOCOMPILE_CMD")) { diff --git a/tools/release_stage.py b/tools/release_stage.py index cb87a8f2..daac84e6 100644 --- a/tools/release_stage.py +++ b/tools/release_stage.py @@ -636,10 +636,18 @@ def stage_toolchain(stage, recomp_dir, recomp_tools, recomp_include, dl_cache, # Windows needs the mingw runtime beside the recompiler; a Linux build links # against the system libstdc++ that is already present. if platform_tag == 'win': - if not mingw_bin: - _die('--mingw-bin is required when staging a Windows toolchain') + # A recompiler built against a static CRT (the cmake-clang-v1 emitters) needs no + # runtime DLLs; a gcc-built one does. Copy whatever the named bin dir (or the + # emitter's own directory) holds, and say which case this is. + dll_src = mingw_bin or recomp_dir + copied = 0 for d in ('libgcc_s_seh-1.dll', 'libstdc++-6.dll', 'libwinpthread-1.dll'): - shutil.copy2(os.path.join(mingw_bin, d), os.path.join(toolchain, d)) + p = os.path.join(dll_src, d) + if os.path.isfile(p): + shutil.copy2(p, os.path.join(toolchain, d)) + copied += 1 + log('mingw runtime DLLs staged beside the recompiler: %d (from %s)%s' + % (copied, dll_src, '' if copied else ' -- assuming a static recompiler')) shutil.copy2(os.path.join(recomp_tools, 'compile_overlays.py'), toolchain) tool_inc = _mkdirs(os.path.join(toolchain, 'include'))