The Squirrel path (electron-forge maker-squirrel -> electron-winstaller) required Wine and Mono, plus a 64-bit Wine prefix, to cross-build on Linux. electron-builder's NSIS target uses its own native tooling, so the same cross-build now needs no Wine and no Mono, matching how muSRFlow is packaged. - package.json: add an electron-builder 'build' block (win/nsis, extraResources for the engine and the flat rootfs, files allow-list, nsis options) and point package:win/make:win at 'electron-builder --win nsis'; drop maker-squirrel. - packaging/wsl2/require-rootfs.js: fail the Windows build when the rootfs is missing (electron-builder has no config-time abort hook). - forge.config.js: drop the Squirrel maker and the Windows rootfs guard; forge now builds Linux/macOS only. - README/CHANGELOG: document the NSIS build and that no Wine/Mono is needed. Verified locally: TRIMSP-NL-Workbench-Setup.exe (NSIS, 186 MB) builds with no Wine/Mono; resources/bin/trimspNL and resources/trimspNL-runtime-rootfs.tar.gz land where wsl-runtime.js looks; guard fails on a missing rootfs.
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-builder (NSIS). The
build.win.extraResources entries in package.json copy the rootfs archive flat
into resources/ and the engine to resources/bin/, matching what
wsl-runtime.js looks for. Linux/macOS packaging still uses electron-forge
(forge.config.js); the two tools coexist.
Build the NSIS 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 — packaging/wsl2/require-rootfs.js fails if packaging/wsl2/out/ is
empty, so the .exe can never silently ship without its engine. Build from
Linux/WSL or Windows after building the rootfs first (Windows cannot compile the
Fortran engine or run the rootfs builder).
The installer is written to dist/ as TRIMSP-NL-Workbench-Setup.exe.
Build-host prerequisites
- Building the
.exeon Windows: nothing extra. - Building the
.exeon Linux (cross-build): nothing extra either. electron-builder's NSIS target uses its own native tooling, so no Wine and no Mono are required (unlike the former Squirrel path).
Verifying the shipped .exe
The app 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:
-
The import succeeded and the engine runs in the distribution:
wsl -d trimspNL-runtime -- /opt/trimsp-runtime/bin/trimspNLExpected: the
Usage: trimspNL <run_basename> [ranlux|pcg32|xoshiro]banner. -
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*_.rgeand_Seq_Results.datappear in that folder, proving both the path translation and thewsl.exespawn work without a local display. -
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. AdjustBASE_IMAGEif the engine is rebuilt against a different baseline. wsl.exeavailability is assumed; older Windows builds without WSL2 must install it (wsl --install) first.- The macOS runtime is not built.
- Cross-building the
.exefrom Linux needs no extra tooling (electron-builder NSIS); Windows builds need none either.