Skip to content

Commit 918a317

Browse files
committed
fix(nsis): build portable in parallel to nsis
Close #1340
1 parent 3f8caab commit 918a317

File tree

8 files changed

+173
-68
lines changed

8 files changed

+173
-68
lines changed

docs/Developer API.md

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
<dd></dd>
6262
<dt><a href="#module_electron-builder/out/targets/targetFactory">electron-builder/out/targets/targetFactory</a></dt>
6363
<dd></dd>
64-
<dt><a href="#module_electron-builder/out/targets/WebInstaller">electron-builder/out/targets/WebInstaller</a></dt>
64+
<dt><a href="#module_electron-builder/out/targets/WebInstallerTarget">electron-builder/out/targets/WebInstallerTarget</a></dt>
6565
<dd></dd>
6666
<dt><a href="#module_electron-builder/out/util/filter">electron-builder/out/util/filter</a></dt>
6767
<dd></dd>
@@ -2081,12 +2081,38 @@ Portable Specific Options ([portable](#Config-portable})
20812081
## electron-builder/out/targets/nsis
20822082

20832083
* [electron-builder/out/targets/nsis](#module_electron-builder/out/targets/nsis)
2084+
* [.AppPackageHelper](#AppPackageHelper)
2085+
* [`.finishBuild()`](#module_electron-builder/out/targets/nsis.AppPackageHelper+finishBuild) ⇒ <code>Promise&lt;any&gt;</code>
2086+
* [`.packArch(arch, target)`](#module_electron-builder/out/targets/nsis.AppPackageHelper+packArch) ⇒ <code>Promise&lt;string&gt;</code>
20842087
* [.NsisTarget](#NsisTarget) ⇐ <code>[Target](#Target)</code>
20852088
* [`.build(appOutDir, arch)`](#module_electron-builder/out/targets/nsis.NsisTarget+build) ⇒ <code>Promise&lt;void&gt;</code>
20862089
* [`.finishBuild()`](#module_electron-builder/out/targets/nsis.NsisTarget+finishBuild) ⇒ <code>Promise&lt;any&gt;</code>
20872090
* [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/nsis.NsisTarget+configureDefines) ⇒ <code>Promise&lt;void&gt;</code>
20882091
* [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/nsis.NsisTarget+generateGitHubInstallerName) ⇒ <code>string</code>
20892092

2093+
<a name="AppPackageHelper"></a>
2094+
2095+
### AppPackageHelper
2096+
**Kind**: class of <code>[electron-builder/out/targets/nsis](#module_electron-builder/out/targets/nsis)</code>
2097+
2098+
* [.AppPackageHelper](#AppPackageHelper)
2099+
* [`.finishBuild()`](#module_electron-builder/out/targets/nsis.AppPackageHelper+finishBuild) ⇒ <code>Promise&lt;any&gt;</code>
2100+
* [`.packArch(arch, target)`](#module_electron-builder/out/targets/nsis.AppPackageHelper+packArch) ⇒ <code>Promise&lt;string&gt;</code>
2101+
2102+
<a name="module_electron-builder/out/targets/nsis.AppPackageHelper+finishBuild"></a>
2103+
2104+
#### `appPackageHelper.finishBuild()` ⇒ <code>Promise&lt;any&gt;</code>
2105+
**Kind**: instance method of <code>[AppPackageHelper](#AppPackageHelper)</code>
2106+
<a name="module_electron-builder/out/targets/nsis.AppPackageHelper+packArch"></a>
2107+
2108+
#### `appPackageHelper.packArch(arch, target)` ⇒ <code>Promise&lt;string&gt;</code>
2109+
**Kind**: instance method of <code>[AppPackageHelper](#AppPackageHelper)</code>
2110+
2111+
| Param | Type |
2112+
| --- | --- |
2113+
| arch | <code>[Arch](#Arch)</code> |
2114+
| target | <code>[NsisTarget](#NsisTarget)</code> |
2115+
20902116
<a name="NsisTarget"></a>
20912117

20922118
### NsisTarget ⇐ <code>[Target](#Target)</code>
@@ -2306,41 +2332,61 @@ Portable Specific Options ([portable](#Config-portable})
23062332
| packager | <code>[PlatformPackager](#PlatformPackager)&lt;any&gt;</code> |
23072333
| cleanupTasks | <code>Array&lt;module:electron-builder/out/targets/targetFactory.__type&gt;</code> |
23082334

2309-
<a name="module_electron-builder/out/targets/WebInstaller"></a>
2335+
<a name="module_electron-builder/out/targets/WebInstallerTarget"></a>
23102336

2311-
## electron-builder/out/targets/WebInstaller
2337+
## electron-builder/out/targets/WebInstallerTarget
23122338

2313-
* [electron-builder/out/targets/WebInstaller](#module_electron-builder/out/targets/WebInstaller)
2314-
* [.WebInstallerTarget](#WebInstallerTarget) ⇐ <code>module:electron-builder/out/targets/nsis.default</code>
2315-
* [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+configureDefines) ⇒ <code>Promise&lt;void&gt;</code>
2316-
* [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+generateGitHubInstallerName) ⇒ <code>string</code>
2339+
* [electron-builder/out/targets/WebInstallerTarget](#module_electron-builder/out/targets/WebInstallerTarget)
2340+
* [.WebInstallerTarget](#WebInstallerTarget) ⇐ <code>[NsisTarget](#NsisTarget)</code>
2341+
* [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+configureDefines) ⇒ <code>Promise&lt;void&gt;</code>
2342+
* [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+generateGitHubInstallerName) ⇒ <code>string</code>
2343+
* [`.build(appOutDir, arch)`](#module_electron-builder/out/targets/nsis.NsisTarget+build) ⇒ <code>Promise&lt;void&gt;</code>
2344+
* [`.finishBuild()`](#module_electron-builder/out/targets/nsis.NsisTarget+finishBuild) ⇒ <code>Promise&lt;any&gt;</code>
23172345

23182346
<a name="WebInstallerTarget"></a>
23192347

2320-
### WebInstallerTarget ⇐ <code>module:electron-builder/out/targets/nsis.default</code>
2321-
**Kind**: class of <code>[electron-builder/out/targets/WebInstaller](#module_electron-builder/out/targets/WebInstaller)</code>
2322-
**Extends**: <code>module:electron-builder/out/targets/nsis.default</code>
2348+
### WebInstallerTarget ⇐ <code>[NsisTarget](#NsisTarget)</code>
2349+
**Kind**: class of <code>[electron-builder/out/targets/WebInstallerTarget](#module_electron-builder/out/targets/WebInstallerTarget)</code>
2350+
**Extends**: <code>[NsisTarget](#NsisTarget)</code>
23232351

2324-
* [.WebInstallerTarget](#WebInstallerTarget) ⇐ <code>module:electron-builder/out/targets/nsis.default</code>
2325-
* [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+configureDefines) ⇒ <code>Promise&lt;void&gt;</code>
2326-
* [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+generateGitHubInstallerName) ⇒ <code>string</code>
2352+
* [.WebInstallerTarget](#WebInstallerTarget) ⇐ <code>[NsisTarget](#NsisTarget)</code>
2353+
* [`.configureDefines(oneClick, defines)`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+configureDefines) ⇒ <code>Promise&lt;void&gt;</code>
2354+
* [`.generateGitHubInstallerName()`](#module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+generateGitHubInstallerName) ⇒ <code>string</code>
2355+
* [`.build(appOutDir, arch)`](#module_electron-builder/out/targets/nsis.NsisTarget+build) ⇒ <code>Promise&lt;void&gt;</code>
2356+
* [`.finishBuild()`](#module_electron-builder/out/targets/nsis.NsisTarget+finishBuild) ⇒ <code>Promise&lt;any&gt;</code>
23272357

2328-
<a name="module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+configureDefines"></a>
2358+
<a name="module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+configureDefines"></a>
23292359

23302360
#### `webInstallerTarget.configureDefines(oneClick, defines)` ⇒ <code>Promise&lt;void&gt;</code>
23312361
**Kind**: instance method of <code>[WebInstallerTarget](#WebInstallerTarget)</code>
2362+
**Overrides**: <code>[configureDefines](#module_electron-builder/out/targets/nsis.NsisTarget+configureDefines)</code>
23322363
**Access**: protected
23332364

23342365
| Param | Type |
23352366
| --- | --- |
23362367
| oneClick | <code>boolean</code> |
23372368
| defines | <code>any</code> |
23382369

2339-
<a name="module_electron-builder/out/targets/WebInstaller.WebInstallerTarget+generateGitHubInstallerName"></a>
2370+
<a name="module_electron-builder/out/targets/WebInstallerTarget.WebInstallerTarget+generateGitHubInstallerName"></a>
23402371

23412372
#### `webInstallerTarget.generateGitHubInstallerName()` ⇒ <code>string</code>
23422373
**Kind**: instance method of <code>[WebInstallerTarget](#WebInstallerTarget)</code>
2374+
**Overrides**: <code>[generateGitHubInstallerName](#module_electron-builder/out/targets/nsis.NsisTarget+generateGitHubInstallerName)</code>
23432375
**Access**: protected
2376+
<a name="module_electron-builder/out/targets/nsis.NsisTarget+build"></a>
2377+
2378+
#### `webInstallerTarget.build(appOutDir, arch)` ⇒ <code>Promise&lt;void&gt;</code>
2379+
**Kind**: instance method of <code>[WebInstallerTarget](#WebInstallerTarget)</code>
2380+
2381+
| Param | Type |
2382+
| --- | --- |
2383+
| appOutDir | <code>string</code> |
2384+
| arch | <code>[Arch](#Arch)</code> |
2385+
2386+
<a name="module_electron-builder/out/targets/nsis.NsisTarget+finishBuild"></a>
2387+
2388+
#### `webInstallerTarget.finishBuild()` ⇒ <code>Promise&lt;any&gt;</code>
2389+
**Kind**: instance method of <code>[WebInstallerTarget](#WebInstallerTarget)</code>
23442390
<a name="module_electron-builder/out/util/filter"></a>
23452391

23462392
## electron-builder/out/util/filter

packages/electron-builder/src/targets/WebInstaller.ts renamed to packages/electron-builder/src/targets/WebInstallerTarget.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { NsisWebOptions } from "../options/winOptions"
22
import { computeDownloadUrl, getPublishConfigs, getPublishConfigsForUpdateInfo } from "../publish/PublishManager"
33
import { WinPackager } from "../winPackager"
4-
import NsisTarget from "./nsis"
4+
import { AppPackageHelper, NsisTarget } from "./nsis"
55

6-
export default class WebInstallerTarget extends NsisTarget {
7-
constructor(packager: WinPackager, outDir: string, targetName: string) {
8-
super(packager, outDir, targetName)
6+
export class WebInstallerTarget extends NsisTarget {
7+
constructor(packager: WinPackager, outDir: string, targetName: string, packageHelper: AppPackageHelper) {
8+
super(packager, outDir, targetName, packageHelper)
99
}
1010

11-
protected get isWebInstaller(): boolean {
11+
get isWebInstaller(): boolean {
1212
return true
1313
}
1414

packages/electron-builder/src/targets/nsis.ts

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,64 @@ const nsisResourcePathPromise = getBinFromBintray("nsis-resources", "3.0.0", "cd
2525

2626
const USE_NSIS_BUILT_IN_COMPRESSOR = false
2727

28-
export default class NsisTarget extends Target {
28+
interface PackageFileInfo {
29+
file: string
30+
}
31+
32+
export class AppPackageHelper {
33+
private readonly archToFileInfo = new Map<Arch, Promise<PackageFileInfo>>()
34+
private readonly infoToIsDelete = new Map<PackageFileInfo, boolean>()
35+
36+
/** @private */
37+
refCount = 0
38+
39+
async packArch(arch: Arch, target: NsisTarget) {
40+
let infoPromise = this.archToFileInfo.get(arch)
41+
if (infoPromise == null) {
42+
infoPromise = subTask(`Packaging NSIS installer for arch ${Arch[arch]}`, target.buildAppPackage(target.archs.get(arch)!, arch))
43+
.then(it => {return {file: it} })
44+
this.archToFileInfo.set(arch, infoPromise)
45+
}
46+
47+
const info = await infoPromise
48+
if (target.isWebInstaller) {
49+
this.infoToIsDelete.set(info, false)
50+
}
51+
else if (!this.infoToIsDelete.has(info)) {
52+
this.infoToIsDelete.set(info, true)
53+
}
54+
return info.file
55+
}
56+
57+
async finishBuild(): Promise<any> {
58+
if (--this.refCount > 0) {
59+
return
60+
}
61+
62+
const filesToDelete: Array<string> = []
63+
for (let [info, isDelete] of this.infoToIsDelete.entries()) {
64+
if (isDelete) {
65+
filesToDelete.push(info.file)
66+
}
67+
}
68+
69+
await BluebirdPromise.map(filesToDelete, it => unlink(it))
70+
}
71+
}
72+
73+
export class NsisTarget extends Target {
2974
readonly options: NsisOptions
3075

31-
private archs: Map<Arch, string> = new Map()
76+
/** @private */
77+
readonly archs: Map<Arch, string> = new Map()
3278

3379
private readonly nsisTemplatesDir = path.join(__dirname, "..", "..", "templates", "nsis")
3480

35-
constructor(protected readonly packager: WinPackager, readonly outDir: string, targetName: string) {
81+
constructor(protected readonly packager: WinPackager, readonly outDir: string, targetName: string, protected readonly packageHelper: AppPackageHelper) {
3682
super(targetName)
3783

84+
this.packageHelper.refCount++
85+
3886
let options = this.packager.config.nsis || Object.create(null)
3987
if (targetName !== "nsis") {
4088
options = Object.assign(options, (<any>this.packager.config)[targetName === "nsis-web" ? "nsisWeb" : targetName])
@@ -51,7 +99,8 @@ export default class NsisTarget extends Target {
5199
this.archs.set(arch, appOutDir)
52100
}
53101

54-
private async buildAppPackage(appOutDir: string, arch: Arch) {
102+
/** @private */
103+
async buildAppPackage(appOutDir: string, arch: Arch) {
55104
await BluebirdPromise.all([
56105
copyFile(path.join(await nsisPathPromise, "elevate.exe"), path.join(appOutDir, "resources", "elevate.exe"), null, false),
57106
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 {
66115
// noinspection JSUnusedGlobalSymbols
67116
async finishBuild(): Promise<any> {
68117
log("Building NSIS installer")
69-
const filesToDelete: Array<string> = []
70118
try {
71-
await this.buildInstaller(filesToDelete)
119+
await this.buildInstaller()
72120
}
73121
finally {
74-
if (filesToDelete.length > 0) {
75-
await BluebirdPromise.map(filesToDelete, it => unlink(it))
76-
}
122+
await this.packageHelper.finishBuild()
77123
}
78124
}
79125

@@ -85,7 +131,7 @@ export default class NsisTarget extends Target {
85131
return this.name === "portable"
86132
}
87133

88-
private async buildInstaller(filesToDelete: Array<string>): Promise<any> {
134+
private async buildInstaller(): Promise<any> {
89135
const isPortable = this.isPortable
90136

91137
const packager = this.packager
@@ -140,16 +186,13 @@ export default class NsisTarget extends Target {
140186
}
141187
else {
142188
await BluebirdPromise.map(this.archs.keys(), async arch => {
143-
const file = await subTask(`Packaging NSIS installer for arch ${Arch[arch]}`, this.buildAppPackage(this.archs.get(arch)!, arch))
189+
const file = await this.packageHelper.packArch(arch, this, )
144190
defines[arch === Arch.x64 ? "APP_64" : "APP_32"] = file
145191
defines[(arch === Arch.x64 ? "APP_64" : "APP_32") + "_NAME"] = path.basename(file)
146192

147193
if (this.isWebInstaller) {
148194
packager.dispatchArtifactCreated(file, this, arch)
149195
}
150-
else {
151-
filesToDelete.push(file)
152-
}
153196
})
154197
}
155198

@@ -195,7 +238,7 @@ export default class NsisTarget extends Target {
195238
return this.options.unicode == null ? true : this.options.unicode
196239
}
197240

198-
protected get isWebInstaller(): boolean {
241+
get isWebInstaller(): boolean {
199242
return false
200243
}
201244

packages/electron-builder/src/winPackager.ts

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import { WinBuildOptions } from "./options/winOptions"
1010
import { BuildInfo } from "./packagerApi"
1111
import { PlatformPackager } from "./platformPackager"
1212
import AppXTarget from "./targets/appx"
13-
import NsisTarget from "./targets/nsis"
13+
import { AppPackageHelper, NsisTarget } from "./targets/nsis"
1414
import { createCommonTarget } from "./targets/targetFactory"
15+
import { WebInstallerTarget } from "./targets/WebInstallerTarget"
1516
import { FileCodeSigningInfo, getSignVendorPath, sign, SignOptions } from "./windowsCodeSign"
1617

1718
export class WinPackager extends PlatformPackager<WinBuildOptions> {
@@ -94,36 +95,46 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
9495
}
9596

9697
createTargets(targets: Array<string>, mapper: (name: string, factory: (outDir: string) => Target) => void, cleanupTasks: Array<() => Promise<any>>): void {
98+
let helper: AppPackageHelper | null
99+
const getHelper = () => {
100+
if (helper == null) {
101+
helper = new AppPackageHelper()
102+
}
103+
return helper
104+
}
105+
97106
for (const name of targets) {
98107
if (name === DIR_TARGET) {
99108
continue
100109
}
101110

102-
const targetClass: typeof NsisTarget | typeof AppXTarget | null = (() => {
103-
switch (name) {
104-
case "nsis":
105-
case "portable":
106-
return require("./targets/nsis").default
107-
case "nsis-web":
108-
return require("./targets/WebInstaller").default
109-
110-
case "squirrel":
111-
try {
112-
return require("electron-builder-squirrel-windows").default
113-
}
114-
catch (e) {
115-
throw new Error(`Module electron-builder-squirrel-windows must be installed in addition to build Squirrel.Windows: ${e.stack || e}`)
116-
}
117-
118-
case "appx":
119-
return require("./targets/appx").default
120-
121-
default:
122-
return null
123-
}
124-
})()
125-
126-
mapper(name, outDir => targetClass === null ? createCommonTarget(name, outDir, this) : new (<any>targetClass)(this, outDir, name))
111+
if (name === "nsis" || name === "portable") {
112+
mapper(name, outDir => new NsisTarget(this, outDir, name, getHelper()))
113+
}
114+
else if (name === "nsis-web") {
115+
mapper(name, outDir => new WebInstallerTarget(this, outDir, name, getHelper()))
116+
}
117+
else {
118+
const targetClass: typeof NsisTarget | typeof AppXTarget | null = (() => {
119+
switch (name) {
120+
case "squirrel":
121+
try {
122+
return require("electron-builder-squirrel-windows").default
123+
}
124+
catch (e) {
125+
throw new Error(`Module electron-builder-squirrel-windows must be installed in addition to build Squirrel.Windows: ${e.stack || e}`)
126+
}
127+
128+
case "appx":
129+
return require("./targets/appx").default
130+
131+
default:
132+
return null
133+
}
134+
})()
135+
136+
mapper(name, outDir => targetClass === null ? createCommonTarget(name, outDir, this) : new (<any>targetClass)(this, outDir, name))
137+
}
127138
}
128139
}
129140

test/out/linux/__snapshots__/debTest.js.snap

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ Object {
8585
"Section": "devel",
8686
"Size": "89541",
8787
"Vendor": "Foo Bar <foo@example.com>",
88-
"Version": "1.1.0-42",
8988
}
9089
`;
9190

@@ -186,7 +185,6 @@ Object {
186185
"Section": "devel",
187186
"Size": "123301",
188187
"Vendor": "Foo Bar <foo@example.com>",
189-
"Version": "1.1.0-42",
190188
}
191189
`;
192190

@@ -275,6 +273,5 @@ Object {
275273
"Section": "devel",
276274
"Size": "123301",
277275
"Vendor": "Foo Bar <foo@example.com>",
278-
"Version": "1.1.0-42",
279276
}
280277
`;

test/out/windows/__snapshots__/portableTest.js.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ Object {
1515
exports[`portable 1`] = `
1616
Object {
1717
"win": Array [
18+
Object {
19+
"arch": 1,
20+
"file": "Test App ßW Setup 1.1.0.exe",
21+
"safeArtifactName": "TestApp-Setup-1.1.0.exe",
22+
},
1823
Object {
1924
"arch": 1,
2025
"file": "Test App ßW 1.1.0.exe",

0 commit comments

Comments
 (0)