Files
salman 752c8363bf Document the win64/WoW64 Wine prefix needed to cross-build the .exe
electron-winstaller's Squirrel runs as 32-bit .NET but zips the package by
ShellExecuteEx-ing the 64-bit vendor/7z.exe. A 32-bit-only Wine prefix
('#arch=win32') cannot launch a 64-bit PE, so the cross-build fails with
'ShellExecuteEx failed: Bad EXE format'. Record the WINEARCH=win64 recipe
(and the pre-existing Wine+Mono requirement) instead of leaving it as a
build-host surprise.
2026-10-20 11:56:25 +02:00
..
2026-10-20 09:31:25 +02:00

Windows (WSL2) engine runtime

Windows has no native TRIM.SP-NL engine; the Linux engine cannot be run directly. Instead TRIMSP-NL Workbench ships the same statically linked engine used by the Linux bundle and runs it inside a bundled WSL2 distribution.

How it works

TrimSPelec.js detects the Windows host and the bundled engine (process.platform === "win32" and <resources>/bin/trimspNL present) and routes the engine through wsl.exe:

wsl.exe -d trimspNL-runtime -- /opt/trimsp-runtime/bin/trimspNL <run> [rng]

Path arguments (C:\dir\file) are translated to their WSL mount form (/mnt/c/dir/file) so the engine reads and writes the user's Windows files. Run inputs and outputs therefore stay on the Windows side.

A user-configured engine binaries directory with a Windows-native executable is launched directly, and on non-Windows hosts the translation is inert, so the Linux build and development checkouts are unaffected.

Constants (in TrimSPelec.js): the distribution name trimspNL-runtime and the in-distro prefix /opt/trimsp-runtime.

Building the rootfs

Requires Docker or Podman and the static engine (npm run build:fortran, built automatically when missing).

npm run build:wsl2          # packaging/wsl2/build-wsl2.sh

Because trimspNL-static is a fully static executable (no libc/libgfortran runtime dependencies) the rootfs is just a minimal base filesystem plus /opt/trimsp-runtime/bin/trimspNL. The script writes /etc/wsl.conf and exports the container filesystem to packaging/wsl2/out/trimspNL-runtime-rootfs.tar.gz.

The script refuses to run if resources/bin/trimspNL is not statically linked.

Importing on Windows

The import is automated. On first run main.js (through wsl-runtime.js) checks whether the trimspNL-runtime distribution is registered and, if not, imports the bundled archive into %LOCALAPPDATA%\TrimSP\trimspNL-runtime with wsl --import ... --version 2. The work happens in the background so the window opens immediately; progress and failures are broadcast to the renderer on the wslRuntimeStatus channel. A failed import resets so a later ensureWslRuntime retries. Before running the engine the renderer awaits window.TrimSPRuntimeGate, so the first run after startup shows a "Preparing the bundled engine runtime" notice and proceeds once the distribution is ready; the gate is a no-op off Windows.

The equivalent manual command, useful for debugging:

wsl --import trimspNL-runtime "$env:LOCALAPPDATA\TrimSP\trimspNL-runtime" trimspNL-runtime-rootfs.tar.gz --version 2

Verify:

wsl -d trimspNL-runtime -- /opt/trimsp-runtime/bin/trimspNL

Packaging

The Windows build ships the rootfs through electron-forge. forge.config.js adds the archive to packagerConfig.extraResource when it exists (copied flat into resources/), while the engine marker at resources/bin/trimspNL lets the renderer detect the bundled runtime and route through WSL.

Build the Squirrel Setup.exe, which contains the app and the WSL2 rootfs:

npm run make:win

This runs the whole chain: it compiles the static engine, builds the rootfs, then packages and makes the installer for win32. A Windows build requires the rootfs — forge.config.js throws if packaging/wsl2/out/ is empty, so the .exe can never silently ship without its engine. Build from Linux/WSL first (Windows cannot compile the Fortran engine or run the rootfs builder).

The installer is written to out/make/squirrel.windows/x64/ as TRIMSP-NL-Workbench-Setup.exe.

Build-host prerequisites

  • Building the .exe on Windows: nothing extra.

  • Building the .exe on Linux (cross-build): electron-winstaller needs both Wine and Mono installed, otherwise it fails with You must install both Mono and Wine on non-Windows. Install e.g. wine and mono-complete (or mono-core) first.

    It also needs a 64-bit (WoW64) Wine prefix. Squirrel is a 32-bit .NET assembly but zips the package by ShellExecuteEx-ing the 64-bit node_modules/electron-winstaller/vendor/7z.exe; a 32-bit-only prefix (#arch=win32) cannot launch a 64-bit PE and the build fails with ShellExecuteEx failed: Bad EXE format. Build against a WoW64 prefix without disturbing an existing one:

    export WINEARCH=win64 WINEPREFIX="$HOME/.wine-trimsp64"
    wineboot -u
    npm run make:win
    

    (Wine ≥ 9 supports running 32-bit apps from a win64 prefix, so a single prefix is enough.) On Windows none of this applies.

Verifying the shipped .exe

The TrimSP.exe inside the install loads the engine only through WSL, so the same manual remote-session check below applies after installing the .exe.

Tests

npm run test:wsl-runtime     # tests/wsl-runtime.test.js + tests/runtime-gate.test.js

Covers the pure main-process module (with an injected wsl.exe spawner), the renderer-side launch translation, including inertness on Linux, and that the scan pipeline awaits the runtime gate before launching the engine.

Manual remote-session smoke test

The engine path is a console process and never touches the display, so it cannot misroute a window the way the file dialogs could. It is still worth checking once on a real Windows host, and especially over SSH/RDP, since that is how the workbench is often driven remotely. After the first-run import, verify:

  1. The import succeeded and the engine runs in the distribution:

    wsl -d trimspNL-runtime -- /opt/trimsp-runtime/bin/trimspNL
    

    Expected: the Usage: trimspNL <run_basename> [ranlux|pcg32|xoshiro] banner.

  2. Over a remote session (SSH/RDP, not just a local console), launch the app, set a Work Folder on a Windows drive, and click Run. Expected: <prefix>_E*_.rge and _Seq_Results.dat appear in that folder, proving both the path translation and the wsl.exe spawn work without a local display.

  3. The status bar first shows "Preparing the bundled engine runtime (first run only)..." and the run then proceeds, proving the runtime gate waited for the import rather than racing it.

Open items

  • The rootfs is built from almalinux:8. Adjust BASE_IMAGE if the engine is rebuilt against a different baseline.
  • wsl.exe availability is assumed; older Windows builds without WSL2 must install it (wsl --install) first.
  • The macOS runtime is not built.
  • Cross-building the .exe from Linux additionally needs Wine, Mono and a 64-bit (WoW64) Wine prefix (see above); building on Windows needs none of these.