Open PeterDaveHello opened 4 months ago
It seems this feature doesn't just work on 32-bit(x86/ia32) + 64-bit(amd64), as the doc mentioned, but also on arm64+amd64, arm64+amd64+ia32, and maybe other combinations.
These are the screenshots from using 7-zip to open the $PLUGINSDIR\
folder in the NSIS installers in different combinations:
So I must admit I don't have the original insight as to why the installers are always combined. I had a project in the past where it was required to have two distinct installers, one for each bitness, though and I implemented an electron-builder script using the programmatic API. I think it looked something akin to the code below (just make sure your artifact name uses the {arch}
macro so that the second build
command doesn't overwrite the artifact of the first one.)
https://www.electron.build/api/programmatic-usage
await builder.build({
targets: platform.createTarget("nsis", 'x64')
config: options
})
await builder.build({
targets: platform.createTarget("nsis", 'ia32')
config: options
})
Sure we can do it by different approach like using command line to build them separately, but a simple option would be also nice 😄
Can you share your electron-builder config?
Try this win
config for your electron-builder configuration. I was able to build separate installers for x64 and arm64 in this manner.
win: {
target: [
{
target: 'nsis',
arch: 'x64'
},
{
target: 'nsis',
arch: 'arm64'
}
],
}
+1 for having a config value for this.
I'm building better-sqlite3 and it needs to be built once per architecture. The combined installer would take the last built better-sqlite3 which would fail for the other archs.
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.
Revisiting this request. I'll see what I can do but the logic in NsisTarget is extremely complex 😅
Update: I've added a configuration property to allow disabling universal builds, but it's only available to nsis
. nsis-web
requires universal in order for it to be able to determine what build to install. The difficulty in implementation is that nsis-web
target extends NsisTarget
, so the logic needed to be conditional and overridden for nsis-web
target.
The PR introduces a config property buildUniversalInstaller
(default true for backward compatibility)
In order to build separate exe's, I needed to update the installer name template to include an arch. Unless a defaultArch
is supplied in the electron-builder config, x64 will not have a suffix appended to the file name (backward compatibility) and the other arch builds will have suffix -${arch}.${ext}
target: [ { target: 'nsis', arch: 'x64' }, { target: 'nsis', arch: 'arm64' } ],
That electron-builder.json
config for me still builds the universal win32 installer. How do I disable that?
• electron-builder version=24.13.3 os=23.6.0
• loaded configuration file=./desktop-wakatime/electron-builder.json
• writing effective config file=release/builder-effective-config.yaml
• packaging platform=win32 arch=x64 electron=32.0.0 appOutDir=release/win-unpacked
• packaging platform=win32 arch=arm64 electron=32.0.0 appOutDir=release/win-arm64-unpacked
• building target=nsis file=release/wakatime-win32.exe archs=x64, arm64 oneClick=false perMachine=false
• building block map blockMapFile=release/wakatime-win32.exe.blockmap
• building target=nsis file=release/wakatime-win32-x64.exe archs=x64 oneClick=false perMachine=false
• building block map blockMapFile=release/wakatime-win32-x64.exe.blockmap
• building target=nsis file=release/wakatime-win32-arm64.exe archs=arm64 oneClick=false perMachine=false
• building block map blockMapFile=release/wakatime-win32-arm64.exe.blockmap
Would anyone be willing to test out this patch-package?
I'm unable to test for arch combinations including ia32
, so I need a person to test with "arm64", "x64", "ia32"
, as ia32
isn't package-able on my mac (arm64 limitation IIRC)
Would also appreciate anyone trying to test out the updater logic, I'm still verifying that myself as well.
app-builder-lib+26.0.0-alpha.3.patch
diff --git a/node_modules/app-builder-lib/out/codeSign/signManager.js b/node_modules/app-builder-lib/out/codeSign/signManager.js
new file mode 100644
index 0000000..09883b3
--- /dev/null
+++ b/node_modules/app-builder-lib/out/codeSign/signManager.js
@@ -0,0 +1,10 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.SignManager = void 0;
+class SignManager {
+ constructor(packager) {
+ this.packager = packager;
+ }
+}
+exports.SignManager = SignManager;
+//# sourceMappingURL=signManager.js.map
\ No newline at end of file
diff --git a/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js b/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js
index e79c555..31aee98 100644
--- a/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js
+++ b/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js
@@ -56,8 +56,15 @@ class NsisTarget extends core_1.Target {
}
nsisUtil_1.NsisTargetOptions.resolve(this.options);
}
+ buildUniversalInstaller() {
+ const buildSeparateInstallers = this.options.buildUniversalInstaller === false;
+ return !buildSeparateInstallers;
+ }
build(appOutDir, arch) {
this.archs.set(arch, appOutDir);
+ if (!this.buildUniversalInstaller()) {
+ return this.buildInstaller(new Map().set(arch, appOutDir));
+ }
return Promise.resolve();
}
get isBuildDifferentialAware() {
@@ -94,7 +101,10 @@ class NsisTarget extends core_1.Target {
return await createPackageFileInfo(archiveFile);
}
}
- get installerFilenamePattern() {
+ installerFilenamePattern(primaryArch, defaultArch) {
+ if (!this.buildUniversalInstaller()) {
+ return "${productName} " + (this.isPortable ? "" : "Setup ") + "${version}" + (primaryArch != null ? (0, builder_util_1.getArchSuffix)(primaryArch, defaultArch) : "") + ".${ext}";
+ }
// tslint:disable:no-invalid-template-strings
return "${productName} " + (this.isPortable ? "" : "Setup ") + "${version}.${ext}";
}
@@ -102,8 +112,11 @@ class NsisTarget extends core_1.Target {
return this.name === "portable";
}
async finishBuild() {
+ if (!this.buildUniversalInstaller()) {
+ return this.packageHelper.finishBuild();
+ }
try {
- const { pattern } = this.packager.artifactPatternConfig(this.options, this.installerFilenamePattern);
+ const { pattern } = this.packager.artifactPatternConfig(this.options, this.installerFilenamePattern());
const builds = new Set([this.archs]);
if (pattern.includes("${arch}") && this.archs.size > 1) {
;
@@ -119,12 +132,13 @@ class NsisTarget extends core_1.Target {
}
}
async buildInstaller(archs) {
- var _a, _b;
+ var _a, _b, _c;
const primaryArch = archs.size === 1 ? ((_a = archs.keys().next().value) !== null && _a !== void 0 ? _a : null) : null;
const packager = this.packager;
const appInfo = packager.appInfo;
const options = this.options;
- const installerFilename = packager.expandArtifactNamePattern(options, "exe", primaryArch, this.installerFilenamePattern, false, this.packager.platformSpecificBuildOptions.defaultArch);
+ const defaultArch = (_b = (0, platformPackager_1.chooseNotNull)(this.packager.platformSpecificBuildOptions.defaultArch, this.packager.config.defaultArch)) !== null && _b !== void 0 ? _b : undefined;
+ const installerFilename = packager.expandArtifactNamePattern(options, "exe", primaryArch, this.installerFilenamePattern(primaryArch, defaultArch), false, defaultArch);
const oneClick = options.oneClick !== false;
const installerPath = path.join(this.outDir, installerFilename);
const logFields = {
@@ -160,7 +174,7 @@ class NsisTarget extends core_1.Target {
BUILD_RESOURCES_DIR: packager.info.buildResourcesDir,
APP_PACKAGE_NAME: (0, targetUtil_1.getWindowsInstallationAppPackageName)(appInfo.name),
};
- if ((_b = options.customNsisBinary) === null || _b === void 0 ? void 0 : _b.debugLogging) {
+ if ((_c = options.customNsisBinary) === null || _c === void 0 ? void 0 : _c.debugLogging) {
defines.ENABLE_LOGGING_ELECTRON_BUILDER = null;
}
if (uninstallAppKey !== guid) {
@@ -279,7 +293,7 @@ class NsisTarget extends core_1.Target {
defines.UNINSTALLER_OUT_FILE = definesUninstaller.UNINSTALLER_OUT_FILE;
await this.executeMakensis(defines, commands, sharedHeader + (await this.computeFinalScript(script, true, archs)));
await Promise.all([packager.sign(installerPath), defines.UNINSTALLER_OUT_FILE == null ? Promise.resolve() : (0, fs_extra_1.unlink)(defines.UNINSTALLER_OUT_FILE)]);
- const safeArtifactName = (0, platformPackager_1.computeSafeArtifactNameIfNeeded)(installerFilename, () => this.generateGitHubInstallerName());
+ const safeArtifactName = (0, platformPackager_1.computeSafeArtifactNameIfNeeded)(installerFilename, () => this.generateGitHubInstallerName(primaryArch, defaultArch));
let updateInfo;
if (this.isWebInstaller) {
updateInfo = (0, differentialUpdateInfoBuilder_1.createNsisWebDifferentialUpdateInfo)(installerPath, packageFiles);
@@ -300,10 +314,11 @@ class NsisTarget extends core_1.Target {
isWriteUpdateInfo: !this.isPortable,
});
}
- generateGitHubInstallerName() {
+ generateGitHubInstallerName(primaryArch, defaultArch) {
const appInfo = this.packager.appInfo;
const classifier = appInfo.name.toLowerCase() === appInfo.name ? "setup-" : "Setup-";
- return `${appInfo.name}-${this.isPortable ? "" : classifier}${appInfo.version}.exe`;
+ const archSuffix = !this.buildUniversalInstaller() && primaryArch != null ? (0, builder_util_1.getArchSuffix)(primaryArch, defaultArch) : "";
+ return `${appInfo.name}-${this.isPortable ? "" : classifier}${appInfo.version}${archSuffix}.exe`;
}
get isUnicodeEnabled() {
return this.options.unicode !== false;
diff --git a/node_modules/app-builder-lib/out/targets/nsis/WebInstallerTarget.js b/node_modules/app-builder-lib/out/targets/nsis/WebInstallerTarget.js
index 9b75baa..b7a707d 100644
--- a/node_modules/app-builder-lib/out/targets/nsis/WebInstallerTarget.js
+++ b/node_modules/app-builder-lib/out/targets/nsis/WebInstallerTarget.js
@@ -1,6 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebInstallerTarget = void 0;
+const builder_util_1 = require("builder-util");
const PublishManager_1 = require("../../publish/PublishManager");
const NsisTarget_1 = require("./NsisTarget");
/** @private */
@@ -27,8 +28,13 @@ class WebInstallerTarget extends NsisTarget_1.NsisTarget {
defines.APP_PACKAGE_URL_IS_INCOMPLETE = null;
defines.APP_PACKAGE_URL = appPackageUrl;
}
- get installerFilenamePattern() {
- // tslint:disable:no-invalid-template-strings
+ buildUniversalInstaller() {
+ if (this.options.buildUniversalInstaller === false) {
+ builder_util_1.log.warn({ buildUniversalInstaller: true }, "only universal builds are supported for nsis-web installers, overriding setting");
+ }
+ return true;
+ }
+ installerFilenamePattern(_primaryArch, _defaultArch) {
return "${productName} Web Setup ${version}.${ext}";
}
generateGitHubInstallerName() {
When building for both 32-bit and 64-bit architectures, Electron Builder creates separate installers for each architecture as well as a combined installer. Is there an option to disable the creation of this combined installer?
Currently, I can't find a way on the doc(https://www.electron.build/configuration/nsis#32-bit-64-bit) to only generate the separate 32-bit and 64-bit installers without also producing the combined one. It would be helpful to have an option like
nsis.disableCombinedInstaller: true
ornsis.enableCombinedInstaller: false
to prevent the creation of the combined installer when it's not needed.Thank you for your assistance.