mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
self-build patch
This commit is contained in:
@@ -19,13 +19,13 @@ then game C, then rebuild links everything.
|
||||
| Piece | Role |
|
||||
|-------|------|
|
||||
| Setup exe | `recomp-ui` + codegen host; opens Generate & rebuild |
|
||||
| `psxrecomp-tools` pack | `psxrecomp_cli.py`, `tools/`, `psxrecomp-game` + `psxrecomp-bios` |
|
||||
| Toolchain pack | RetComM `cmake-clang-v1` (not embedded in game zips) |
|
||||
| Game zip `psxrecomp/` | `psxrecomp_cli.py`, `tools/`, `psxrecomp-game` + `psxrecomp-bios` |
|
||||
| Toolchain pack | RetComM `cmake-clang-v1` (optional under `toolchain/` in game zips) |
|
||||
| Game sources | `game.toml`, seeds, `CMakeLists.txt`, `psxrecomp/`, `recomp-ui/` |
|
||||
|
||||
RetComM can drive the same CLI without the ImGui host. Direct-download zips
|
||||
should embed tools + a real (or smoke) toolchain under `toolchain/` and document
|
||||
Python 3.
|
||||
RetComM harvests emitters from the game zip into a shared SDK cache (no
|
||||
separate tools zip). Direct-download zips are wizard-complete; document
|
||||
Python 3 and cmake/toolchain on PATH (or bundled `toolchain/`).
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
+90
-6
@@ -33,6 +33,69 @@ EXIT_USAGE = 2
|
||||
EXIT_VERIFY = 3
|
||||
|
||||
|
||||
def clamp_future_mtimes(
|
||||
root: Path,
|
||||
*,
|
||||
skip: Optional[Path] = None,
|
||||
now: Optional[float] = None,
|
||||
) -> int:
|
||||
"""Clamp mtimes ahead of *now* so Ninja does not infinite-reconfigure.
|
||||
|
||||
Release zips often preserve CI clocks that are slightly ahead of a user's
|
||||
clock (timezone / skew). Ninja then treats every source as newer than
|
||||
``build.ninja``, re-runs CMake forever, and fails with
|
||||
``manifest 'build.ninja' still dirty after 100 tries``.
|
||||
"""
|
||||
if not root.is_dir():
|
||||
return 0
|
||||
stamp = time.time() if now is None else now
|
||||
skip_res: Optional[Path] = None
|
||||
if skip is not None:
|
||||
try:
|
||||
skip_res = skip.resolve()
|
||||
except OSError:
|
||||
skip_res = skip
|
||||
n = 0
|
||||
for dirpath, dirnames, filenames in os.walk(root, followlinks=False):
|
||||
dpath = Path(dirpath)
|
||||
try:
|
||||
d_res = dpath.resolve()
|
||||
except OSError:
|
||||
d_res = dpath
|
||||
# Skip the active build tree entirely (outputs are rewritten anyway).
|
||||
if skip_res is not None and (
|
||||
d_res == skip_res or skip_res in d_res.parents
|
||||
):
|
||||
dirnames[:] = []
|
||||
continue
|
||||
# Never descend into VCS metadata; prune the build dir at the parent.
|
||||
pruned: list[str] = []
|
||||
for x in dirnames:
|
||||
if x == ".git":
|
||||
continue
|
||||
if skip_res is not None:
|
||||
try:
|
||||
if (dpath / x).resolve() == skip_res:
|
||||
continue
|
||||
except OSError:
|
||||
pass
|
||||
pruned.append(x)
|
||||
dirnames[:] = pruned
|
||||
for name in filenames:
|
||||
p = dpath / name
|
||||
try:
|
||||
mtime = p.stat().st_mtime
|
||||
except OSError:
|
||||
continue
|
||||
if mtime > stamp:
|
||||
try:
|
||||
os.utime(p, (stamp, stamp), follow_symlinks=False)
|
||||
n += 1
|
||||
except OSError:
|
||||
pass
|
||||
return n
|
||||
|
||||
|
||||
def _parse_array_items(inner: str) -> list[Any]:
|
||||
items: list[Any] = []
|
||||
if not inner.strip():
|
||||
@@ -561,18 +624,22 @@ def _cmake_configure(
|
||||
progress: ProgressReporter,
|
||||
) -> None:
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
# Prefer Ninja when available; fall back so hosts without ninja still build.
|
||||
if not (build_dir / "CMakeCache.txt").is_file() and shutil.which("ninja"):
|
||||
gen = ["-G", "Ninja"]
|
||||
else:
|
||||
gen = []
|
||||
cmd = [
|
||||
"cmake",
|
||||
"-S",
|
||||
str(project_root),
|
||||
"-B",
|
||||
str(build_dir),
|
||||
*gen,
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
f"-DPSX_PGO={pgo}",
|
||||
*extra,
|
||||
]
|
||||
# Only force generator on fresh trees (avoid clash with existing cache).
|
||||
if not (build_dir / "CMakeCache.txt").is_file():
|
||||
cmd.extend(["-G", "Ninja"])
|
||||
cmd.append(f"-DPSX_PGO={pgo}")
|
||||
cmd.extend(extra)
|
||||
progress.log(" ".join(cmd))
|
||||
proc = subprocess.run(cmd, cwd=str(project_root), capture_output=True, text=True)
|
||||
for stream in (proc.stdout, proc.stderr):
|
||||
@@ -609,7 +676,17 @@ def _cmake_build(
|
||||
if line.strip():
|
||||
progress.log(line)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"cmake --build failed (exit {proc.returncode})")
|
||||
err = f"cmake --build failed (exit {proc.returncode})"
|
||||
combined = "\n".join(
|
||||
s for s in (proc.stdout or "", proc.stderr or "") if s
|
||||
)
|
||||
if "still dirty after" in combined:
|
||||
err += (
|
||||
" — Ninja reconfigure loop (often release-zip mtimes ahead of "
|
||||
"the system clock). Re-run after the CLI clamps future mtimes, "
|
||||
"or touch the project tree / fix the clock."
|
||||
)
|
||||
raise RuntimeError(err)
|
||||
|
||||
|
||||
def _soft_stop(pid: int, timeout: int = 30) -> None:
|
||||
@@ -823,6 +900,13 @@ def cmd_rebuild(args: argparse.Namespace, progress: ProgressReporter) -> int:
|
||||
cmake_extra.append("-DBPE_FORCE_SETUP_HOST=OFF")
|
||||
cmake_extra.append("-DPSXRECOMP_ALLOW_NO_BIOS=OFF")
|
||||
|
||||
clamped = clamp_future_mtimes(project_root, skip=build_dir)
|
||||
if clamped:
|
||||
progress.log(
|
||||
f"Clamped {clamped} future mtime(s) under {project_root} "
|
||||
"(avoids Ninja dirty-manifest loop from release-zip clocks)."
|
||||
)
|
||||
|
||||
# mute_host_audio / hide_video: default ON; game.toml / CLI can disable.
|
||||
mute_host = True
|
||||
if "mute_host_audio" in pgo:
|
||||
|
||||
Reference in New Issue
Block a user