diff --git a/docs/Developer API.md b/docs/Developer API.md index 9c71a35a123..d28ddeeccbc 100644 --- a/docs/Developer API.md +++ b/docs/Developer API.md @@ -61,7 +61,7 @@
electron-builder/out/targets/targetFactory
-
electron-builder/out/targets/WebInstaller
+
electron-builder/out/targets/WebInstallerTarget
electron-builder/out/util/filter
@@ -2081,12 +2081,38 @@ Portable Specific Options ([portable](#Config-portable}) ## electron-builder/out/targets/nsis * [electron-builder/out/targets/nsis](#module_electron-builder/out/targets/nsis) + * [.AppPackageHelper](#AppPackageHelper) + * [`.finishBuild()`](#module_electron-builder/out/targets/nsis.AppPackageHelper+finishBuild) ⇒ Promise<any> + * [`.packArch(arch, target)`](#module_electron-builder/out/targets/nsis.AppPackageHelper+packArch) ⇒ Promise<string> * [.NsisTarget](#NsisTarget) ⇐ [Target](#Target) * [`.build(appOutDir, arch)`](#module_electron-builder/out/targets/nsis.NsisTarget+build) ⇒ Promise<void> * [`.finishBuild()`](#module_electron-builder/out/targets/nsis.NsisTarget+finishBuild) ⇒ Promise<any> * [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/nsis.NsisTarget+configureDefines) ⇒ Promise<void> * [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/nsis.NsisTarget+generateGitHubInstallerName) ⇒ string + + +### AppPackageHelper +**Kind**: class of [electron-builder/out/targets/nsis](#module_electron-builder/out/targets/nsis) + +* [.AppPackageHelper](#AppPackageHelper) + * [`.finishBuild()`](#module_electron-builder/out/targets/nsis.AppPackageHelper+finishBuild) ⇒ Promise<any> + * [`.packArch(arch, target)`](#module_electron-builder/out/targets/nsis.AppPackageHelper+packArch) ⇒ Promise<string> + + + +#### `appPackageHelper.finishBuild()` ⇒ Promise<any> +**Kind**: instance method of [AppPackageHelper](#AppPackageHelper) + + +#### `appPackageHelper.packArch(arch, target)` ⇒ Promise<string> +**Kind**: instance method of [AppPackageHelper](#AppPackageHelper) + +| Param | Type | +| --- | --- | +| arch | [Arch](#Arch) | +| target | [NsisTarget](#NsisTarget) | + ### NsisTarget ⇐ [Target](#Target) @@ -2306,29 +2332,34 @@ Portable Specific Options ([portable](#Config-portable}) | packager | [PlatformPackager](#PlatformPackager)<any> | | cleanupTasks | Array<module:electron-builder/out/targets/targetFactory.__type> | - + -## electron-builder/out/targets/WebInstaller +## electron-builder/out/targets/WebInstallerTarget -* [electron-builder/out/targets/WebInstaller](#module_electron-builder/out/targets/WebInstaller) - * [.WebInstallerTarget](#WebInstallerTarget) ⇐ module:electron-builder/out/targets/nsis.default - * [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+configureDefines) ⇒ Promise<void> - * [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+generateGitHubInstallerName) ⇒ string +* [electron-builder/out/targets/WebInstallerTarget](#module_electron-builder/out/targets/WebInstallerTarget) + * [.WebInstallerTarget](#WebInstallerTarget) ⇐ [NsisTarget](#NsisTarget) + * [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+configureDefines) ⇒ Promise<void> + * [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+generateGitHubInstallerName) ⇒ string + * [`.build(appOutDir, arch)`](#module_electron-builder/out/targets/nsis.NsisTarget+build) ⇒ Promise<void> + * [`.finishBuild()`](#module_electron-builder/out/targets/nsis.NsisTarget+finishBuild) ⇒ Promise<any> -### WebInstallerTarget ⇐ module:electron-builder/out/targets/nsis.default -**Kind**: class of [electron-builder/out/targets/WebInstaller](#module_electron-builder/out/targets/WebInstaller) -**Extends**: module:electron-builder/out/targets/nsis.default +### WebInstallerTarget ⇐ [NsisTarget](#NsisTarget) +**Kind**: class of [electron-builder/out/targets/WebInstallerTarget](#module_electron-builder/out/targets/WebInstallerTarget) +**Extends**: [NsisTarget](#NsisTarget) -* [.WebInstallerTarget](#WebInstallerTarget) ⇐ module:electron-builder/out/targets/nsis.default - * [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+configureDefines) ⇒ Promise<void> - * [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+generateGitHubInstallerName) ⇒ string +* [.WebInstallerTarget](#WebInstallerTarget) ⇐ [NsisTarget](#NsisTarget) + * [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+configureDefines) ⇒ Promise<void> + * [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+generateGitHubInstallerName) ⇒ string + * [`.build(appOutDir, arch)`](#module_electron-builder/out/targets/nsis.NsisTarget+build) ⇒ Promise<void> + * [`.finishBuild()`](#module_electron-builder/out/targets/nsis.NsisTarget+finishBuild) ⇒ Promise<any> - + #### `webInstallerTarget.configureDefines(oneClick, defines)` ⇒ Promise<void> **Kind**: instance method of [WebInstallerTarget](#WebInstallerTarget) +**Overrides**: [configureDefines](#module_electron-builder/out/targets/nsis.NsisTarget+configureDefines) **Access**: protected | Param | Type | @@ -2336,11 +2367,26 @@ Portable Specific Options ([portable](#Config-portable}) | oneClick | boolean | | defines | any | - + #### `webInstallerTarget.generateGitHubInstallerName()` ⇒ string **Kind**: instance method of [WebInstallerTarget](#WebInstallerTarget) +**Overrides**: [generateGitHubInstallerName](#module_electron-builder/out/targets/nsis.NsisTarget+generateGitHubInstallerName) **Access**: protected + + +#### `webInstallerTarget.build(appOutDir, arch)` ⇒ Promise<void> +**Kind**: instance method of [WebInstallerTarget](#WebInstallerTarget) + +| Param | Type | +| --- | --- | +| appOutDir | string | +| arch | [Arch](#Arch) | + + + +#### `webInstallerTarget.finishBuild()` ⇒ Promise<any> +**Kind**: instance method of [WebInstallerTarget](#WebInstallerTarget) ## electron-builder/out/util/filter diff --git a/packages/electron-builder/src/targets/WebInstaller.ts b/packages/electron-builder/src/targets/WebInstallerTarget.ts similarity index 86% rename from packages/electron-builder/src/targets/WebInstaller.ts rename to packages/electron-builder/src/targets/WebInstallerTarget.ts index 0727e015f37..733c6fbdb32 100644 --- a/packages/electron-builder/src/targets/WebInstaller.ts +++ b/packages/electron-builder/src/targets/WebInstallerTarget.ts @@ -1,14 +1,14 @@ import { NsisWebOptions } from "../options/winOptions" import { computeDownloadUrl, getPublishConfigs, getPublishConfigsForUpdateInfo } from "../publish/PublishManager" import { WinPackager } from "../winPackager" -import NsisTarget from "./nsis" +import { AppPackageHelper, NsisTarget } from "./nsis" -export default class WebInstallerTarget extends NsisTarget { - constructor(packager: WinPackager, outDir: string, targetName: string) { - super(packager, outDir, targetName) +export class WebInstallerTarget extends NsisTarget { + constructor(packager: WinPackager, outDir: string, targetName: string, packageHelper: AppPackageHelper) { + super(packager, outDir, targetName, packageHelper) } - protected get isWebInstaller(): boolean { + get isWebInstaller(): boolean { return true } diff --git a/packages/electron-builder/src/targets/nsis.ts b/packages/electron-builder/src/targets/nsis.ts index 4249d706fa8..cf70a85a778 100644 --- a/packages/electron-builder/src/targets/nsis.ts +++ b/packages/electron-builder/src/targets/nsis.ts @@ -25,16 +25,64 @@ const nsisResourcePathPromise = getBinFromBintray("nsis-resources", "3.0.0", "cd const USE_NSIS_BUILT_IN_COMPRESSOR = false -export default class NsisTarget extends Target { +interface PackageFileInfo { + file: string +} + +export class AppPackageHelper { + private readonly archToFileInfo = new Map>() + private readonly infoToIsDelete = new Map() + + /** @private */ + refCount = 0 + + async packArch(arch: Arch, target: NsisTarget) { + let infoPromise = this.archToFileInfo.get(arch) + if (infoPromise == null) { + infoPromise = subTask(`Packaging NSIS installer for arch ${Arch[arch]}`, target.buildAppPackage(target.archs.get(arch)!, arch)) + .then(it => {return {file: it} }) + this.archToFileInfo.set(arch, infoPromise) + } + + const info = await infoPromise + if (target.isWebInstaller) { + this.infoToIsDelete.set(info, false) + } + else if (!this.infoToIsDelete.has(info)) { + this.infoToIsDelete.set(info, true) + } + return info.file + } + + async finishBuild(): Promise { + if (--this.refCount > 0) { + return + } + + const filesToDelete: Array = [] + for (let [info, isDelete] of this.infoToIsDelete.entries()) { + if (isDelete) { + filesToDelete.push(info.file) + } + } + + await BluebirdPromise.map(filesToDelete, it => unlink(it)) + } +} + +export class NsisTarget extends Target { readonly options: NsisOptions - private archs: Map = new Map() + /** @private */ + readonly archs: Map = new Map() private readonly nsisTemplatesDir = path.join(__dirname, "..", "..", "templates", "nsis") - constructor(protected readonly packager: WinPackager, readonly outDir: string, targetName: string) { + constructor(protected readonly packager: WinPackager, readonly outDir: string, targetName: string, protected readonly packageHelper: AppPackageHelper) { super(targetName) + this.packageHelper.refCount++ + let options = this.packager.config.nsis || Object.create(null) if (targetName !== "nsis") { options = Object.assign(options, (this.packager.config)[targetName === "nsis-web" ? "nsisWeb" : targetName]) @@ -51,7 +99,8 @@ export default class NsisTarget extends Target { this.archs.set(arch, appOutDir) } - private async buildAppPackage(appOutDir: string, arch: Arch) { + /** @private */ + async buildAppPackage(appOutDir: string, arch: Arch) { await BluebirdPromise.all([ copyFile(path.join(await nsisPathPromise, "elevate.exe"), path.join(appOutDir, "resources", "elevate.exe"), null, false), copyFile(path.join(await getSignVendorPath(), "windows-10", Arch[arch], "signtool.exe"), path.join(appOutDir, "resources", "signtool.exe"), null, false), @@ -66,14 +115,11 @@ export default class NsisTarget extends Target { // noinspection JSUnusedGlobalSymbols async finishBuild(): Promise { log("Building NSIS installer") - const filesToDelete: Array = [] try { - await this.buildInstaller(filesToDelete) + await this.buildInstaller() } finally { - if (filesToDelete.length > 0) { - await BluebirdPromise.map(filesToDelete, it => unlink(it)) - } + await this.packageHelper.finishBuild() } } @@ -85,7 +131,7 @@ export default class NsisTarget extends Target { return this.name === "portable" } - private async buildInstaller(filesToDelete: Array): Promise { + private async buildInstaller(): Promise { const isPortable = this.isPortable const packager = this.packager @@ -140,16 +186,13 @@ export default class NsisTarget extends Target { } else { await BluebirdPromise.map(this.archs.keys(), async arch => { - const file = await subTask(`Packaging NSIS installer for arch ${Arch[arch]}`, this.buildAppPackage(this.archs.get(arch)!, arch)) + const file = await this.packageHelper.packArch(arch, this, ) defines[arch === Arch.x64 ? "APP_64" : "APP_32"] = file defines[(arch === Arch.x64 ? "APP_64" : "APP_32") + "_NAME"] = path.basename(file) if (this.isWebInstaller) { packager.dispatchArtifactCreated(file, this, arch) } - else { - filesToDelete.push(file) - } }) } @@ -195,7 +238,7 @@ export default class NsisTarget extends Target { return this.options.unicode == null ? true : this.options.unicode } - protected get isWebInstaller(): boolean { + get isWebInstaller(): boolean { return false } diff --git a/packages/electron-builder/src/winPackager.ts b/packages/electron-builder/src/winPackager.ts index 81e3efde76f..e222b9f32db 100644 --- a/packages/electron-builder/src/winPackager.ts +++ b/packages/electron-builder/src/winPackager.ts @@ -10,8 +10,9 @@ import { WinBuildOptions } from "./options/winOptions" import { BuildInfo } from "./packagerApi" import { PlatformPackager } from "./platformPackager" import AppXTarget from "./targets/appx" -import NsisTarget from "./targets/nsis" +import { AppPackageHelper, NsisTarget } from "./targets/nsis" import { createCommonTarget } from "./targets/targetFactory" +import { WebInstallerTarget } from "./targets/WebInstallerTarget" import { FileCodeSigningInfo, getSignVendorPath, sign, SignOptions } from "./windowsCodeSign" export class WinPackager extends PlatformPackager { @@ -94,36 +95,46 @@ export class WinPackager extends PlatformPackager { } createTargets(targets: Array, mapper: (name: string, factory: (outDir: string) => Target) => void, cleanupTasks: Array<() => Promise>): void { + let helper: AppPackageHelper | null + const getHelper = () => { + if (helper == null) { + helper = new AppPackageHelper() + } + return helper + } + for (const name of targets) { if (name === DIR_TARGET) { continue } - const targetClass: typeof NsisTarget | typeof AppXTarget | null = (() => { - switch (name) { - case "nsis": - case "portable": - return require("./targets/nsis").default - case "nsis-web": - return require("./targets/WebInstaller").default - - case "squirrel": - try { - return require("electron-builder-squirrel-windows").default - } - catch (e) { - throw new Error(`Module electron-builder-squirrel-windows must be installed in addition to build Squirrel.Windows: ${e.stack || e}`) - } - - case "appx": - return require("./targets/appx").default - - default: - return null - } - })() - - mapper(name, outDir => targetClass === null ? createCommonTarget(name, outDir, this) : new (targetClass)(this, outDir, name)) + if (name === "nsis" || name === "portable") { + mapper(name, outDir => new NsisTarget(this, outDir, name, getHelper())) + } + else if (name === "nsis-web") { + mapper(name, outDir => new WebInstallerTarget(this, outDir, name, getHelper())) + } + else { + const targetClass: typeof NsisTarget | typeof AppXTarget | null = (() => { + switch (name) { + case "squirrel": + try { + return require("electron-builder-squirrel-windows").default + } + catch (e) { + throw new Error(`Module electron-builder-squirrel-windows must be installed in addition to build Squirrel.Windows: ${e.stack || e}`) + } + + case "appx": + return require("./targets/appx").default + + default: + return null + } + })() + + mapper(name, outDir => targetClass === null ? createCommonTarget(name, outDir, this) : new (targetClass)(this, outDir, name)) + } } } diff --git a/test/out/linux/__snapshots__/debTest.js.snap b/test/out/linux/__snapshots__/debTest.js.snap index 6813ecc0b15..94c929f2f64 100644 --- a/test/out/linux/__snapshots__/debTest.js.snap +++ b/test/out/linux/__snapshots__/debTest.js.snap @@ -85,7 +85,6 @@ Object { "Section": "devel", "Size": "89541", "Vendor": "Foo Bar ", - "Version": "1.1.0-42", } `; @@ -186,7 +185,6 @@ Object { "Section": "devel", "Size": "123301", "Vendor": "Foo Bar ", - "Version": "1.1.0-42", } `; @@ -275,6 +273,5 @@ Object { "Section": "devel", "Size": "123301", "Vendor": "Foo Bar ", - "Version": "1.1.0-42", } `; diff --git a/test/out/windows/__snapshots__/portableTest.js.snap b/test/out/windows/__snapshots__/portableTest.js.snap index b0d9166d30e..f4efe955e23 100644 --- a/test/out/windows/__snapshots__/portableTest.js.snap +++ b/test/out/windows/__snapshots__/portableTest.js.snap @@ -15,6 +15,11 @@ Object { exports[`portable 1`] = ` Object { "win": Array [ + Object { + "arch": 1, + "file": "Test App ßW Setup 1.1.0.exe", + "safeArtifactName": "TestApp-Setup-1.1.0.exe", + }, Object { "arch": 1, "file": "Test App ßW 1.1.0.exe", diff --git a/test/src/helpers/packTester.ts b/test/src/helpers/packTester.ts index c50474377e6..f8f4565b087 100755 --- a/test/src/helpers/packTester.ts +++ b/test/src/helpers/packTester.ts @@ -229,7 +229,9 @@ async function checkLinuxResult(outDir: string, packager: Packager, arch: Arch, expect(await getContents(`${outDir}/TestApp_${appInfo.version}_i386.deb`)).toMatchSnapshot() } - expect(parseDebControl(await exec("dpkg", ["--info", packageFile]))).toMatchSnapshot() + const control = parseDebControl(await exec("dpkg", ["--info", packageFile])) + delete control.Version + expect(control).toMatchSnapshot() } function parseDebControl(info: string): any { diff --git a/test/src/windows/portableTest.ts b/test/src/windows/portableTest.ts index bd9c5f81fc2..1c6ac7d684b 100644 --- a/test/src/windows/portableTest.ts +++ b/test/src/windows/portableTest.ts @@ -1,15 +1,16 @@ import { Platform } from "electron-builder" import { app } from "../helpers/packTester" -test.ifNotCiMac("portable", app({ - targets: Platform.WINDOWS.createTarget(["portable"]), +// build in parallel - /~https://github.com/electron-userland/electron-builder/issues/1340#issuecomment-286061789 +test.ifAll.ifNotCiMac("portable", app({ + targets: Platform.WINDOWS.createTarget(["portable", "nsis"]), config: { nsis: { } } })) -test.ifAll.ifNotCiMac("portable - artifactName and request execution level", app({ +test.ifNotCiMac("portable - artifactName and request execution level", app({ targets: Platform.WINDOWS.createTarget(["portable"]), config: { "nsis": {