Merge pull request #442 from RetroPortingToolKit/fix/windows-framework-validation-20260930

fix: unblock Windows framework regression builds and tests
This commit is contained in:
Matt Stanley
2026-09-30 14:58:38 -07:00
committed by GitHub
10 changed files with 77 additions and 15 deletions
@@ -11,6 +11,18 @@ import sys
import tempfile
import time
if os.name == 'nt':
import ctypes
# Several publication checks deliberately probe non-PE synthetic DLLs.
# CTest's Windows child error mode can display a loader error dialog and
# block LoadLibrary indefinitely. Keep the real loader/export validation,
# but make its expected failures noninteractive in this test process.
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
kernel32.GetErrorMode.restype = ctypes.c_uint
kernel32.SetErrorMode.argtypes = [ctypes.c_uint]
kernel32.SetErrorMode.restype = ctypes.c_uint
kernel32.SetErrorMode(kernel32.GetErrorMode() | 0x0001 | 0x8000)
ROOT = pathlib.Path(__file__).resolve().parents[2]
SPEC = importlib.util.spec_from_file_location(
+1 -2
View File
@@ -342,8 +342,7 @@ if(BUILD_TESTING)
target_compile_options(gpu_textured_dot_nw_shift_exec_test PRIVATE
-ffunction-sections -fdata-sections)
target_link_options(gpu_textured_dot_nw_shift_exec_test PRIVATE
-Wl,--gc-sections
-Wl,--error-limit=0)
-Wl,--gc-sections)
endif()
add_test(NAME gpu_textured_dot_nw_shift_exec_test
COMMAND gpu_textured_dot_nw_shift_exec_test)
+6 -1
View File
@@ -1629,7 +1629,12 @@ function(psxrecomp_add_runtime_target target)
string(REPLACE "\\" "/" _psxrt_ico_fwd "${PSXRT_APP_ICON}")
set(_psxrt_rc "${CMAKE_CURRENT_BINARY_DIR}/${target}_app_icon.rc")
file(WRITE "${_psxrt_rc}" "IDI_ICON1 ICON \"${_psxrt_ico_fwd}\"\n")
target_sources(${target} PRIVATE "${_psxrt_rc}")
# The icon has no dependency on runtime macros or headers.
# Compile it separately: windres launches a preprocessor through
# a shell, which cannot round-trip the executable's quoted
# string definitions (notably the pipe-separated BIOS stems).
add_library(${target}_app_icon OBJECT "${_psxrt_rc}")
target_sources(${target} PRIVATE $<TARGET_OBJECTS:${target}_app_icon>)
message(STATUS "psxrecomp ${target}: APP_ICON=${PSXRT_APP_ICON} (RC=${CMAKE_RC_COMPILER})")
else()
message(WARNING
+5 -3
View File
@@ -26,9 +26,11 @@ def function_body(name: str) -> str:
reinsert = function_body("debug_force_cd_reinsert")
assert "cdrom_lid_begin_open" in reinsert
assert "s_lid_irq_pending = 1" in reinsert
assert "set_irq(CDIRQ_ACK)" not in reinsert
assert "begin_tray_open_cycle()" in reinsert
tray_open = function_body("begin_tray_open_cycle")
assert "cdrom_lid_begin_open" in tray_open
assert "s_lid_irq_pending = 1" in tray_open
assert "set_irq(CDIRQ_ACK)" not in reinsert + tray_open
present = function_body("present_lid_open_irq_if_ready")
assert "response_push(0x08)" in present
@@ -15,6 +15,24 @@ uint32_t g_debug_current_func_addr;
uint32_t g_debug_last_store_pc;
CPUState *debug_cpu_ptr;
int g_psx_vram_dirty_tracking;
uint32_t g_psx_ram_size = 0x00200000u;
uint32_t g_psx_ram_mask = 0x001FFFFFu;
/* This fixture exercises stock GPU packets without a GL context or mod bank.
* Keep the gpu.c dependencies explicit, including on linkers that retain
* additional exported command handlers despite --gc-sections. */
GrBackend gr_backend(void) { return GR_BACKEND_SOFTWARE; }
int gl_renderer_texture_banks_supported(void) { return 0; }
int gl_renderer_select_texture_bank(uint16_t id) { (void)id; return 0; }
uint16_t mod_texture_packet_bank(uint32_t source, const uint32_t *words,
uint32_t count) {
(void)source; (void)words; (void)count;
return 0;
}
int mod_texture_packet_precision(uint32_t source, float q[3], float xy[6]) {
(void)source; (void)q; (void)xy;
return 0;
}
static uint32_t test_ram[0x00200000u / 4u];
+2 -1
View File
@@ -46,7 +46,8 @@ int main(int argc, char** argv) {
env = os.environ.copy()
compiler = shutil.which(args.compiler) or args.compiler
env['PATH'] = str(Path(compiler).parent) + os.pathsep + env.get('PATH', '')
subprocess.run([compiler, '-std=c++17', '-O2', '-I' + str(root/'include'), str(source),
subprocess.run([compiler, '-std=c++17', '-O2', '-I' + str(root/'include'),
'-I' + str(root.parent/'recompiler/include'), str(source),
str(root/'src/disc_path.cpp'), str(root/'src/cue_sheet.cpp'), '-o', str(program)],
check=True, env=env)
for stem, tracks in [('disc', 12), ('album', 8)]:
+1
View File
@@ -124,6 +124,7 @@ def run_select(
driver = tmp / "select_driver.cmake"
driver.write_text(
'cmake_minimum_required(VERSION 3.20)\n'
f'include("{SELECT.as_posix()}")\n'
"psx_select_builtin_mod_ids(_out LABEL psx-runtime"
+ cm_list("AVAILABLE", available)
@@ -104,7 +104,7 @@ SCANNED_NAMES = ('CMakeLists.txt',)
SKIP_DIRS = {'.git', 'build', 'generated', 'node_modules', '__pycache__',
'lib', '_deps', 'beetle-psx', 'recomp-ui', 'psxrecomp-v4',
'ghidra', 'seeds', 'assets'}
'ghidra', 'seeds', 'assets', 'overlay_toolchain'}
# A release packager that touches the OVERLAY CACHE must route through the
# framework's shared staging surface rather than reimplementing it. The
@@ -128,6 +128,17 @@ TAG_CONSUMER_RE = re.compile(
SHARED_SURFACE_RE = re.compile(r'release_overlay_stage|release_stage\.py')
def check_consumer_detection():
# Config validation is not cache staging. Retain positive controls so the
# narrower heuristic cannot silently exempt real cache consumers.
assert not TAG_CONSUMER_RE.search('overlay_cache = true')
assert not TAG_CONSUMER_RE.search('${SHIP_WITHOUT_OVERLAY_CACHE_BECAUSE}')
for example in ('${overlay_cache}', '$overlay_cache', '$Overlay_Cache',
'overlay_cache/gcc', 'cache_tag()', '$CgTag', 'cg_tag=foo',
'overlay_toolchain'):
assert TAG_CONSUMER_RE.search(example), example
def _is_scanned(name):
return name.endswith(SCANNED_SUFFIXES) or name in SCANNED_NAMES
@@ -178,6 +189,7 @@ def framework_root():
def main(argv=None):
check_consumer_detection()
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
ap.add_argument('--root', action='append', default=[],
help='extra tree to check (repeatable). In an extra tree '
@@ -11,12 +11,14 @@ REWIND = (ROOT / "src/psx_rewind.c").read_text(encoding="utf-8")
DIRTY = (ROOT / "src/dirty_ram_interp.c").read_text(encoding="utf-8")
BOOT_STATE_H = (ROOT / "include/boot_state.h").read_text(encoding="utf-8")
DMA = (ROOT / "src/dma.c").read_text(encoding="utf-8")
BOOT_STATE = (ROOT / "src/boot_state.c").read_text(encoding="utf-8")
# The per-word DMA2 cursor and XA DATA_END pending bit grow the snapshot wire.
# Lock the format change to v7 so an old file is rejected before any state
# section is applied.
# Keep the v7 minimum: v8 adds enhancement memory, while vanilla writers still
# emit v7. Older files must be rejected before any live state is applied.
assert "#define DMA_GPU_LL_WIRE (4u + (10u * 4u))" in DMA
assert "#define BOOT_STATE_VERSION 7u" in BOOT_STATE_H
assert "#define BOOT_STATE_VERSION 8u" in BOOT_STATE_H
assert "psx_mod_memory_snapshot_bytes() ? BOOT_STATE_VERSION : 7u" in BOOT_STATE
assert "#define BOOT_STATE_VERSION_MIN_READ 7u" in BOOT_STATE_H
assert "Reject\n * them at the header before any section changes the live machine." in BOOT_STATE_H
+14 -4
View File
@@ -33,10 +33,20 @@ sys.path.insert(0, str(TOOLKIT))
TEMPLATE = FW / "docs" / "ci" / "templates" / "game-release.yml"
CHECK_GENERATED = FW / "tools" / "ci" / "check_generated.sh"
BOOT = "SLUS_012.34"
GIT = shutil.which("git")
BASH = shutil.which("bash")
if os.name == "nt":
# PATH can resolve bash to the WSL launcher and git to an MSYS shim.
# Neither reliably accepts the native Windows fixture/script paths.
git_root = Path(os.environ.get("ProgramFiles", "C:/Program Files")) / "Git"
if (git_root / "cmd/git.exe").is_file():
GIT = str(git_root / "cmd/git.exe")
if (git_root / "bin/bash.exe").is_file():
BASH = str(git_root / "bin/bash.exe")
def _git(root, *args):
return subprocess.run(["git", "-C", str(root), *args], capture_output=True,
return subprocess.run([GIT, "-C", str(root), *args], capture_output=True,
text=True, check=False)
@@ -167,7 +177,7 @@ class ScaffoldContract(unittest.TestCase):
class CheckGeneratedScript(unittest.TestCase):
def run_check(self, root):
return subprocess.run(["bash", str(CHECK_GENERATED), "--root", str(root)],
return subprocess.run([BASH, CHECK_GENERATED.as_posix(), "--root", root.as_posix()],
capture_output=True, text=True, check=False)
def test_passes_on_committed_game_c(self):
@@ -278,12 +288,12 @@ class PackagerScriptContract(unittest.TestCase):
"tools/ci/generate_openbios.sh"):
p = FW / rel
self.assertTrue(os.access(p, os.X_OK), f"{rel} not executable")
r = subprocess.run(["bash", "-n", str(p)], capture_output=True, text=True)
r = subprocess.run([BASH, "-n", p.as_posix()], capture_output=True, text=True)
self.assertEqual(r.returncode, 0, r.stderr)
if __name__ == "__main__":
if shutil.which("git") is None or shutil.which("bash") is None:
if GIT is None or BASH is None:
print("git and bash are required", file=sys.stderr)
sys.exit(2)
unittest.main(verbosity=1)