New CMake preset `cross-win-clangcl` for Ninja Multi-Config on non-Windows hosts. Toolchain: clang-cl (MSVC-ABI), lld-link, xwin SDK/CRT splat. Shader compilation routes through wine fxc.exe with FXC_PATH env forwarding and unix→Windows path translation via `winepath -w`. Plus per-file build fixes (constexpr→const, llvm-rc forward-slash, zlib-ng AVX guard, /RTCsu MSVC-only). third_party/snappy bumped to fabi/sylpheed-crossbuild for the target-aware POSIX-gate header. Produces `xenia_canary.exe` (Debug MSVC) suitable for use as the audit oracle under Wine for xenia-rs work. See docs/CROSS_BUILD_SETUP.md for the full reproduction recipe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.6 KiB
Building and Running Xenia Canary Windows Debug from Linux
What we built and why
A Windows-MSVC debug xenia_canary.exe cross-compiled on Linux, runnable under Wine. This serves as the audit oracle for the xenia-rs Rust port: the Linux-native canary build stalls before the front-end UI, so all audit comparisons (memory entries audit_044–audit_059) need the Wine-targeted debug binary as the ground-truth reference engine.
Phase 1 — Host setup (run in your terminal)
sudo apt install -y clang-18 lld-18 llvm-18 # clang-cl driver mode + lld-link + binutils
sudo ln -s /usr/bin/clang-18 /usr/bin/clang-cl # activates MSVC-compatible driver mode
xwin --accept-license --arch x86_64 splat --include-debug-libs --output ~/.xwin/splat
# ~807 MB Win10 SDK + MSVC CRT sysroot
curl ... linkid=2361406 -o ~/winsdk.iso # Win10 SDK ISO for real fxc.exe (SM 5_1)
winetricks -q vkd3d # vkd3d-proton d3d12core.dll (5.96 MB)
winetricks -q dxvk # DXVK dxgi.dll (2.95 MB)
Ubuntu's clang-18 / lld-18 apt packages ship binutils suffixed (llvm-lib-18, lld-link-18, etc.); the toolchain expects unsuffixed names on PATH. Create 4 symlinks in ~/.local/bin/ (no sudo needed since it was already on PATH).
Phase 2 — In-tree edits
8 build-fix patches (compile-time only; zero runtime semantics changes)
| § | File | What changed |
|---|---|---|
| 7.1 | src/xenia/base/mapped_memory_win.cc:30 |
constexpr → const for kFileHandleInvalid (clang-cl rejects reinterpret_cast in constant expressions) |
| 7.2 | src/xenia/app/main_resources.rc:3 |
..\\..\\..\\assets\\icon\\icon.ico → forward slashes (llvm-rc backslash literalism) |
| 7.3 | third_party/snappy/snappy-stubs-public.h |
Already in place via the fabi/sylpheed-crossbuild snappy fork submodule |
| 7.4 | third_party/zlib-ng/zconf-ng.h:113 |
#if 1 → #if !defined(_WIN32) for Z_HAVE_UNISTD_H (header was pre-generated on Linux) |
| 7.5 | third_party/CMakeLists.txt:335 |
Extended if(NOT MSVC) → if(NOT MSVC OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") for zlib-ng AVX flags |
| 7.6a | CMakeLists.txt |
Wrapped /RTCsu appends in CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC" AND NOT … "Clang" |
| 7.6b | CMakeLists.txt:85-101 |
Inserted execute_process block invoking xenia-build.py:generate_version_h() so CMake-direct flow produces version.h |
| 7.7 | cmake/XeniaHelpers.cmake |
Added _env_prefix to wrap per-shader commands in cmake -E env FXC_PATH=… (cmake set(ENV{…}) doesn't survive ninja's subprocess spawn) |
| 7.8 | tools/build/compile_shader_dxbc.py:74 |
Added _wineify() helper translating unix paths via winepath -w before fxc sees them (fxc parses /home/… as a /h switch) |
3 new cross-compile config files
| File | Purpose |
|---|---|
cmake/toolchains/linux-to-win-msvc.cmake |
clang-cl + lld-link + xwin sysroot driver. Forces MultiThreadedDLL runtime (no debug-CRT DLLs needed), feeds -imsvc flags via SHELL: prefix, suppresses 15+ clang-cl-specific warnings, defines _mm_cvtsi64x_si128 alias, opts out of MSVC STL's Clang≥19 assertion, plumbs FXC_PATH |
cmake/toolchains/xwin-case-symlinks.py |
Scans xenia includes for mixed-case Windows headers and creates the missing case-aliases in xwin's flat splat (5 needed: ObjBase.h, Psapi.h, etc.) |
CMakePresets.json |
Added cross-win-clangcl configure preset + cross-debug / cross-release / cross-checked build presets, gated on ${hostSystemName} ≠ Windows |
Phase 3 — Configure + build
python3 cmake/toolchains/xwin-case-symlinks.py "$PWD" "$HOME/.xwin/splat"
# Created 5 case-symlinks across 556 unique includes
cmake --preset cross-win-clangcl
# Generates build-cross/ with build-Debug.ninja + auto-generated version.h
cmake --build build-cross --preset cross-debug --target xenia-app -j6
# 936 ninja targets. -j6 not -j12 because host RAM is tight (~3.4 GiB free at start)
# Wall time ~25 min total
Final output: xenia_canary.exe (27 MB PE32+ GUI x86-64) + xenia_canary.pdb (116 MB CodeView).
Phase 4 — Running the game
cd "/home/fabi/RE Project Sylpheed/xenia-canary/build-cross/bin/Windows/Debug"
WINEDEBUG=-all wine ./xenia_canary.exe --mute=true \
"/home/fabi/RE Project Sylpheed/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso"
Kill after the boot phase completes (the audit workflow only needs ~30 s of trajectory):
sleep 30
wineserver -k
Expected boot signals in xenia.log
| Signal | Value | Meaning |
|---|---|---|
| Total log lines | 1091 | Healthy boot (≥1000 = passes) |
^x> fatals |
0 | No xe::FatalError() triggered |
^!> errors |
28 | Non-fatal (occasional pipeline misses — normal) |
^w> warnings |
28 | Normal startup |
i> Setup: Initializing chain |
Memory → Exports → Processor → Audio → Graphics → HID → VFS → Kernel | All eight subsystems came up |
i> Setup: Starting graphics_system + Starting audio_system |
present | Threads spawned cleanly |
| DXGI adapter | NVIDIA GeForce GTX 1070 Ti | vkd3d-proton + DXVK loaded |
K> XThread::Execute thid N count |
29 | Guest threads spawned (game is past XexLoadImage) |
Skipping draw - pipeline not ready: VS … PS … repeating |
yes | D3D12 backend is async-compiling pipelines — expected on cold boot |
F> channel |
321 lines | VFS is mounting and walking the disc image |
K> channel |
29 lines | Kernel dispatching XThread creation |
Audit-workflow integration
The cross-build is now the sole canary oracle. The Linux-native xenia-canary/build/bin/Linux/Debug/xenia_canary is deprecated for audits.
All canary launches must include --mute=true (project policy 2026-05-12). XAudio2 still initializes — same code paths, silent output. Do not substitute --apu=nop; that switches backend entirely.
Point logs at audit-runs with --log_file=:
LOG="$XENIA_RS_ROOT/xenia-rs/audit-runs/audit_NN/canary.log" \
WINEDEBUG=-all wine xenia_canary.exe \
--mute=true --log_file="$LOG" \
--audit_NN_my_probe=true \
"$ISO_PATH"
Add new audit instrumentation as cvars in cpu_flags.h / gpu_flags.h / kernel_flags.h, guard with if (cvars::audit_NN_my_probe), emit XELOGI("AUDIT-NN-EVENT …"). Rebuild incrementally — only the touched TU recompiles, ≤60 s.
The build is observation-only: cvars + XELOG*() + #if-gated diagnostic blocks are permitted; changing control flow, return values, struct layouts, or timing is forbidden, since that would invalidate every downstream audit comparison.