Build the Windows installer with electron-builder (NSIS)
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.
This commit is contained in:
1 parent
752c8363bf
commit
5110ea4827
6 files changed
+1655
-125
No files matched your search
+4
-3
@@ -10,9 +10,10 @@
|
||||
on first run, and is launched via `wsl.exe` with Windows paths translated to
|
||||
their `/mnt/<drive>` form. A runtime gate waits for the import before the
|
||||
first run, and progress is reported in the status bar.
|
||||
- A Squirrel Windows installer (`npm run make:win`,
|
||||
`TRIMSP-NL-Workbench-Setup.exe`) that bundles the WSL2 runtime; a Windows
|
||||
build refuses to proceed without it.
|
||||
- An NSIS Windows installer (`npm run make:win`,
|
||||
`TRIMSP-NL-Workbench-Setup.exe`) built with electron-builder that bundles the
|
||||
WSL2 runtime; the build refuses to proceed without it. Linux/macOS packaging
|
||||
continues to use electron-forge.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
+1
-44
@@ -1,5 +1,4 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const redhatInstaller = require('electron-installer-redhat');
|
||||
const redhatDependencies = require('electron-installer-redhat/src/dependencies');
|
||||
|
||||
@@ -18,42 +17,8 @@ const linuxDesktopTemplate = path.resolve(__dirname, 'packaging/linux/trimsp.des
|
||||
const linuxRpmSpecTemplate = path.resolve(__dirname, 'packaging/linux/trimsp.spec.ejs');
|
||||
const includeFlatpak = process.env.FORGE_FLATPAK === '1';
|
||||
|
||||
// Windows cannot run the bundled Linux engine natively. The Windows build also
|
||||
// ships the engine as a WSL2 rootfs archive (built by packaging/wsl2) which the
|
||||
// app imports as a private distribution on first run.
|
||||
const wsl2Rootfs = path.resolve(__dirname, 'packaging/wsl2/out/trimspNL-runtime-rootfs.tar.gz');
|
||||
|
||||
// Detect a Windows build so we can (a) include the rootfs and (b) refuse to
|
||||
// produce a Windows installer that would be missing its engine. Forge is asked
|
||||
// for a target platform either as `--platform win32` / `-p win32` (make/package)
|
||||
// or implicitly when running on Windows itself.
|
||||
function wantsWindowsTarget() {
|
||||
if (process.platform === 'win32') return true;
|
||||
const argv = process.argv.slice(2);
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const arg = argv[i];
|
||||
if (arg === '--platform' || arg === '-p') {
|
||||
if ((argv[i + 1] || '').toLowerCase() === 'win32') return true;
|
||||
}
|
||||
for (const prefix of ['--platform=', '-p=']) {
|
||||
if (arg.toLowerCase().startsWith(prefix)
|
||||
&& arg.slice(prefix.length).toLowerCase() === 'win32') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const windowsBuild = wantsWindowsTarget();
|
||||
// The packaged Linux engine.
|
||||
const extraResource = ['resources/bin'];
|
||||
if (fs.existsSync(wsl2Rootfs)) {
|
||||
extraResource.push(wsl2Rootfs);
|
||||
} else if (windowsBuild) {
|
||||
throw new Error(
|
||||
'Windows build requires the WSL2 engine runtime, but\n ' + wsl2Rootfs
|
||||
+ '\nis missing. Build it first from Linux/WSL with:\n npm run build:wsl2');
|
||||
}
|
||||
|
||||
// The upstream RPM maker assumes Fedora-style package names. Mageia provides
|
||||
// GTK 3 under lib64gtk+3_0, so allow either name in the generated Requires.
|
||||
@@ -67,14 +32,6 @@ redhatInstaller.Installer.prototype.createSpec = function () {
|
||||
};
|
||||
|
||||
const makers = [
|
||||
{
|
||||
name: '@electron-forge/maker-squirrel',
|
||||
config: {
|
||||
name: 'TrimSP',
|
||||
setupExe: 'TRIMSP-NL-Workbench-Setup.exe',
|
||||
setupIcon: path.resolve(__dirname, 'icon.ico'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-zip',
|
||||
platforms: ['darwin'],
|
||||
|
||||
Generated
+1556
-39
File diff suppressed because it is too large.
Load diff
+51
-4
@@ -16,8 +16,8 @@
|
||||
"make:flatpak": "npm run build:fortran && FORGE_FLATPAK=1 electron-forge make --targets=@electron-forge/maker-flatpak",
|
||||
"build:wsl2": "bash packaging/wsl2/build-wsl2.sh",
|
||||
"test:wsl-runtime": "node tests/wsl-runtime.test.js && node tests/runtime-gate.test.js",
|
||||
"package:win": "npm run build:fortran && npm run build:wsl2 && electron-forge package --platform=win32",
|
||||
"make:win": "npm run build:fortran && npm run build:wsl2 && electron-forge make --platform=win32 --targets=@electron-forge/maker-squirrel",
|
||||
"package:win": "npm run build:fortran && npm run build:wsl2 && node packaging/wsl2/require-rootfs.js && electron-builder --win nsis --x64 --dir",
|
||||
"make:win": "npm run build:fortran && npm run build:wsl2 && node packaging/wsl2/require-rootfs.js && electron-builder --win nsis --x64",
|
||||
"checksums:md5": "sh -c 'for d in out/make/deb/x64 out/make/rpm/x64 out/make/flatpak/x86_64; do [ -d \"$d\" ] || continue; for f in \"$d\"/*; do [ -f \"$f\" ] || continue; case \"$f\" in *.deb|*.rpm|*.flatpak) base=$(basename \"$f\"); (cd \"$d\" && md5sum \"$base\" > \"$base.md5\");; esac; done; done'",
|
||||
"checksums:sha256": "sh -c 'for d in out/make/deb/x64 out/make/rpm/x64 out/make/flatpak/x86_64; do [ -d \"$d\" ] || continue; for f in \"$d\"/*; do [ -f \"$f\" ] || continue; case \"$f\" in *.deb|*.rpm|*.flatpak) base=$(basename \"$f\"); (cd \"$d\" && sha256sum \"$base\" > \"$base.sha256\");; esac; done; done'",
|
||||
"checksums": "npm run checksums:md5 && npm run checksums:sha256"
|
||||
@@ -37,9 +37,9 @@
|
||||
"@electron-forge/maker-deb": "7.11.2",
|
||||
"@electron-forge/maker-flatpak": "7.11.2",
|
||||
"@electron-forge/maker-rpm": "7.11.2",
|
||||
"@electron-forge/maker-squirrel": "7.11.2",
|
||||
"@electron-forge/maker-zip": "7.11.2",
|
||||
"electron": "44.9.0"
|
||||
"electron": "44.9.0",
|
||||
"electron-builder": "26.15.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"plotly.js-dist": "^1.58.4"
|
||||
@@ -49,5 +49,52 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitea.psi.ch/LMU/TRIMSP.git"
|
||||
},
|
||||
"build": {
|
||||
"appId": "ch.psi.lem.trimsp",
|
||||
"productName": "TRIMSP-NL Workbench",
|
||||
"artifactName": "TRIMSP-NL-Workbench-Setup.${ext}",
|
||||
"asar": true,
|
||||
"directories": {
|
||||
"output": "dist"
|
||||
},
|
||||
"files": [
|
||||
"main.js",
|
||||
"wsl-runtime.js",
|
||||
"TrimSP.html",
|
||||
"quick-start.html",
|
||||
"ZGUI.css",
|
||||
"plotly.css",
|
||||
"TrimSPweb.js",
|
||||
"TrimSPelec.js",
|
||||
"TrimSPlib.js",
|
||||
"myplots.js",
|
||||
"appicons/**/*",
|
||||
"node_modules/**/*"
|
||||
],
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis"
|
||||
],
|
||||
"icon": "icon.ico",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "resources/bin",
|
||||
"to": "bin"
|
||||
},
|
||||
{
|
||||
"from": "packaging/wsl2/out",
|
||||
"to": ".",
|
||||
"filter": [
|
||||
"trimspNL-runtime-rootfs.tar.gz"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"perMachine": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-35
@@ -69,12 +69,13 @@ 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.
|
||||
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 Squirrel `Setup.exe`, which contains the app **and** the WSL2 rootfs:
|
||||
Build the NSIS `Setup.exe`, which contains the app **and** the WSL2 rootfs:
|
||||
|
||||
```sh
|
||||
npm run make:win
|
||||
@@ -82,41 +83,24 @@ 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).
|
||||
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 `out/make/squirrel.windows/x64/` as
|
||||
`TRIMSP-NL-Workbench-Setup.exe`.
|
||||
The installer is written to `dist/` 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:
|
||||
|
||||
```sh
|
||||
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.
|
||||
- Building the `.exe` **on 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 `TrimSP.exe` inside the install loads the engine only through WSL, so the
|
||||
same manual remote-session check below applies after installing the `.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
|
||||
@@ -160,6 +144,5 @@ workbench is often driven remotely. After the first-run import, verify:
|
||||
- `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.
|
||||
- Cross-building the `.exe` from Linux needs no extra tooling (electron-builder
|
||||
NSIS); Windows builds need none either.
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env node
|
||||
// Refuse to build the Windows installer without the WSL2 engine runtime.
|
||||
//
|
||||
// Windows cannot run the bundled Linux engine natively, so the installer must
|
||||
// ship the rootfs archive (packaging/wsl2/out/trimspNL-runtime-rootfs.tar.gz)
|
||||
// that the app imports as a private WSL2 distribution on first run. Building
|
||||
// without it would silently produce an installer whose engine cannot start.
|
||||
//
|
||||
// electron-builder has no config-time hook that can abort the build, so the
|
||||
// `dist:win` script runs this first.
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const rootfs = path.resolve(
|
||||
__dirname, 'out', 'trimspNL-runtime-rootfs.tar.gz');
|
||||
|
||||
if (!fs.existsSync(rootfs)) {
|
||||
console.error(
|
||||
'Windows build requires the WSL2 engine runtime, but\n ' + rootfs
|
||||
+ '\nis missing. Build it first from Linux/WSL with:\n npm run build:wsl2');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('WSL2 rootfs found: ' + rootfs);
|
||||
Reference in new issue
Block a user