mirror of
https://gitee.com/myxzgzs/boyue_jnpf.git
synced 2025-08-12 17:22:42 +08:00
693 lines
36 KiB
JavaScript
693 lines
36 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.NsisTarget = void 0;
|
|
const builder_util_1 = require("builder-util");
|
|
const builder_util_runtime_1 = require("builder-util-runtime");
|
|
const debug_1 = require("debug");
|
|
const fs = require("fs");
|
|
const fs_extra_1 = require("fs-extra");
|
|
const path = require("path");
|
|
const binDownload_1 = require("../../binDownload");
|
|
const core_1 = require("../../core");
|
|
const CommonWindowsInstallerConfiguration_1 = require("../../options/CommonWindowsInstallerConfiguration");
|
|
const platformPackager_1 = require("../../platformPackager");
|
|
const hash_1 = require("../../util/hash");
|
|
const macosVersion_1 = require("../../util/macosVersion");
|
|
const timer_1 = require("../../util/timer");
|
|
const wine_1 = require("../../wine");
|
|
const archive_1 = require("../archive");
|
|
const differentialUpdateInfoBuilder_1 = require("../differentialUpdateInfoBuilder");
|
|
const targetUtil_1 = require("../targetUtil");
|
|
const nsisLang_1 = require("./nsisLang");
|
|
const nsisLicense_1 = require("./nsisLicense");
|
|
const nsisScriptGenerator_1 = require("./nsisScriptGenerator");
|
|
const nsisUtil_1 = require("./nsisUtil");
|
|
const debug = (0, debug_1.default)("electron-builder:nsis");
|
|
// noinspection SpellCheckingInspection
|
|
const ELECTRON_BUILDER_NS_UUID = builder_util_runtime_1.UUID.parse("50e065bc-3134-11e6-9bab-38c9862bdaf3");
|
|
// noinspection SpellCheckingInspection
|
|
const nsisResourcePathPromise = () => (0, binDownload_1.getBinFromUrl)("nsis-resources", "3.4.1", "Dqd6g+2buwwvoG1Vyf6BHR1b+25QMmPcwZx40atOT57gH27rkjOei1L0JTldxZu4NFoEmW4kJgZ3DlSWVON3+Q==");
|
|
const USE_NSIS_BUILT_IN_COMPRESSOR = false;
|
|
class NsisTarget extends core_1.Target {
|
|
constructor(packager, outDir, targetName, packageHelper) {
|
|
super(targetName);
|
|
this.packager = packager;
|
|
this.outDir = outDir;
|
|
this.packageHelper = packageHelper;
|
|
/** @private */
|
|
this.archs = new Map();
|
|
this.isAsyncSupported = false;
|
|
this.packageHelper.refCount++;
|
|
this.options =
|
|
targetName === "portable"
|
|
? Object.create(null)
|
|
: {
|
|
preCompressedFileExtensions: [".avi", ".mov", ".m4v", ".mp4", ".m4p", ".qt", ".mkv", ".webm", ".vmdk"],
|
|
...this.packager.config.nsis,
|
|
};
|
|
if (targetName !== "nsis") {
|
|
Object.assign(this.options, this.packager.config[targetName === "nsis-web" ? "nsisWeb" : targetName]);
|
|
}
|
|
const deps = packager.info.metadata.dependencies;
|
|
if (deps != null && deps["electron-squirrel-startup"] != null) {
|
|
builder_util_1.log.warn('"electron-squirrel-startup" dependency is not required for NSIS');
|
|
}
|
|
nsisUtil_1.NsisTargetOptions.resolve(this.options);
|
|
}
|
|
get shouldBuildUniversalInstaller() {
|
|
const buildSeparateInstallers = this.options.buildUniversalInstaller === false;
|
|
return !buildSeparateInstallers;
|
|
}
|
|
build(appOutDir, arch) {
|
|
this.archs.set(arch, appOutDir);
|
|
if (!this.shouldBuildUniversalInstaller) {
|
|
return this.buildInstaller(new Map().set(arch, appOutDir));
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
get isBuildDifferentialAware() {
|
|
return !this.isPortable && this.options.differentialPackage !== false;
|
|
}
|
|
getPreCompressedFileExtensions() {
|
|
const result = this.isWebInstaller ? null : this.options.preCompressedFileExtensions;
|
|
return result == null ? null : (0, builder_util_1.asArray)(result).map(it => (it.startsWith(".") ? it : `.${it}`));
|
|
}
|
|
/** @private */
|
|
async buildAppPackage(appOutDir, arch) {
|
|
const options = this.options;
|
|
const packager = this.packager;
|
|
const isBuildDifferentialAware = this.isBuildDifferentialAware;
|
|
const format = !isBuildDifferentialAware && options.useZip ? "zip" : "7z";
|
|
const archiveFile = path.join(this.outDir, `${packager.appInfo.sanitizedName}-${packager.appInfo.version}-${builder_util_1.Arch[arch]}.nsis.${format}`);
|
|
const preCompressedFileExtensions = this.getPreCompressedFileExtensions();
|
|
const archiveOptions = {
|
|
withoutDir: true,
|
|
compression: packager.compression,
|
|
excluded: preCompressedFileExtensions == null ? null : preCompressedFileExtensions.map(it => `*${it}`),
|
|
};
|
|
const timer = (0, timer_1.time)(`nsis package, ${builder_util_1.Arch[arch]}`);
|
|
await (0, archive_1.archive)(format, archiveFile, appOutDir, isBuildDifferentialAware ? (0, differentialUpdateInfoBuilder_1.configureDifferentialAwareArchiveOptions)(archiveOptions) : archiveOptions);
|
|
timer.end();
|
|
if (isBuildDifferentialAware && this.isWebInstaller) {
|
|
const data = await (0, differentialUpdateInfoBuilder_1.appendBlockmap)(archiveFile);
|
|
return {
|
|
...data,
|
|
path: archiveFile,
|
|
};
|
|
}
|
|
else {
|
|
return await createPackageFileInfo(archiveFile);
|
|
}
|
|
}
|
|
installerFilenamePattern(primaryArch, defaultArch) {
|
|
const setupText = this.isPortable ? "" : "Setup ";
|
|
const archSuffix = !this.shouldBuildUniversalInstaller && primaryArch != null ? (0, builder_util_1.getArchSuffix)(primaryArch, defaultArch) : "";
|
|
return "${productName} " + setupText + "${version}" + archSuffix + ".${ext}";
|
|
}
|
|
get isPortable() {
|
|
return this.name === "portable";
|
|
}
|
|
async finishBuild() {
|
|
if (!this.shouldBuildUniversalInstaller) {
|
|
return this.packageHelper.finishBuild();
|
|
}
|
|
try {
|
|
const { pattern } = this.packager.artifactPatternConfig(this.options, this.installerFilenamePattern());
|
|
const builds = new Set([this.archs]);
|
|
if (pattern.includes("${arch}") && this.archs.size > 1) {
|
|
;
|
|
[...this.archs].forEach(([arch, appOutDir]) => builds.add(new Map().set(arch, appOutDir)));
|
|
}
|
|
const doBuildArchs = builds.values();
|
|
for (const archs of doBuildArchs) {
|
|
await this.buildInstaller(archs);
|
|
}
|
|
}
|
|
finally {
|
|
await this.packageHelper.finishBuild();
|
|
}
|
|
}
|
|
async buildInstaller(archs) {
|
|
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 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 = {
|
|
target: this.name,
|
|
file: builder_util_1.log.filePath(installerPath),
|
|
archs: Array.from(archs.keys())
|
|
.map(it => builder_util_1.Arch[it])
|
|
.join(", "),
|
|
};
|
|
const isPerMachine = options.perMachine === true;
|
|
if (!this.isPortable) {
|
|
logFields.oneClick = oneClick;
|
|
logFields.perMachine = isPerMachine;
|
|
}
|
|
await packager.info.emitArtifactBuildStarted({
|
|
targetPresentableName: this.name,
|
|
file: installerPath,
|
|
arch: primaryArch,
|
|
}, logFields);
|
|
const guid = options.guid || builder_util_runtime_1.UUID.v5(appInfo.id, ELECTRON_BUILDER_NS_UUID);
|
|
const uninstallAppKey = guid.replace(/\\/g, " - ");
|
|
const defines = {
|
|
APP_ID: appInfo.id,
|
|
APP_GUID: guid,
|
|
// Windows bug - entry in Software\Microsoft\Windows\CurrentVersion\Uninstall cannot have \ symbols (dir)
|
|
UNINSTALL_APP_KEY: uninstallAppKey,
|
|
PRODUCT_NAME: appInfo.productName,
|
|
PRODUCT_FILENAME: appInfo.productFilename,
|
|
APP_FILENAME: (0, targetUtil_1.getWindowsInstallationDirName)(appInfo, !oneClick || isPerMachine),
|
|
APP_DESCRIPTION: appInfo.description,
|
|
VERSION: appInfo.version,
|
|
PROJECT_DIR: packager.projectDir,
|
|
BUILD_RESOURCES_DIR: packager.info.buildResourcesDir,
|
|
APP_PACKAGE_NAME: (0, targetUtil_1.getWindowsInstallationAppPackageName)(appInfo.name),
|
|
};
|
|
if ((_c = options.customNsisBinary) === null || _c === void 0 ? void 0 : _c.debugLogging) {
|
|
defines.ENABLE_LOGGING_ELECTRON_BUILDER = null;
|
|
}
|
|
if (uninstallAppKey !== guid) {
|
|
defines.UNINSTALL_REGISTRY_KEY_2 = `Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${guid}`;
|
|
}
|
|
const commands = {
|
|
OutFile: `"${installerPath}"`,
|
|
VIProductVersion: appInfo.getVersionInWeirdWindowsForm(),
|
|
VIAddVersionKey: this.computeVersionKey(),
|
|
Unicode: this.isUnicodeEnabled,
|
|
};
|
|
const isPortable = this.isPortable;
|
|
const iconPath = (isPortable ? null : await packager.getResource(options.installerIcon, "installerIcon.ico")) || (await packager.getIconPath());
|
|
if (iconPath != null) {
|
|
if (isPortable) {
|
|
commands.Icon = `"${iconPath}"`;
|
|
}
|
|
else {
|
|
defines.MUI_ICON = iconPath;
|
|
defines.MUI_UNICON = iconPath;
|
|
}
|
|
}
|
|
const packageFiles = {};
|
|
let estimatedSize = 0;
|
|
if (this.isPortable && options.useZip) {
|
|
for (const [arch, dir] of archs.entries()) {
|
|
defines[arch === builder_util_1.Arch.x64 ? "APP_DIR_64" : arch === builder_util_1.Arch.arm64 ? "APP_DIR_ARM64" : "APP_DIR_32"] = dir;
|
|
}
|
|
}
|
|
else if (USE_NSIS_BUILT_IN_COMPRESSOR && archs.size === 1) {
|
|
const value = archs.keys().next().value;
|
|
(0, builder_util_1.use)(value, v => (defines.APP_BUILD_DIR = archs.get(v)));
|
|
}
|
|
else {
|
|
await Promise.all(Array.from(archs.keys()).map(async (arch) => {
|
|
const { fileInfo, unpackedSize } = await this.packageHelper.packArch(arch, this);
|
|
const file = fileInfo.path;
|
|
const defineKey = arch === builder_util_1.Arch.x64 ? "APP_64" : arch === builder_util_1.Arch.arm64 ? "APP_ARM64" : "APP_32";
|
|
defines[defineKey] = file;
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
const defineNameKey = `${defineKey}_NAME`;
|
|
defines[defineNameKey] = path.basename(file);
|
|
// nsis expect a hexadecimal string
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
const defineHashKey = `${defineKey}_HASH`;
|
|
defines[defineHashKey] = Buffer.from(fileInfo.sha512, "base64").toString("hex").toUpperCase();
|
|
// NSIS accepts size in KiloBytes and supports only whole numbers
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
const defineUnpackedSizeKey = `${defineKey}_UNPACKED_SIZE`;
|
|
defines[defineUnpackedSizeKey] = Math.ceil(unpackedSize / 1024).toString();
|
|
if (this.isWebInstaller) {
|
|
await packager.dispatchArtifactCreated(file, this, arch);
|
|
packageFiles[builder_util_1.Arch[arch]] = fileInfo;
|
|
}
|
|
const path7za = await (0, builder_util_1.getPath7za)();
|
|
const archiveInfo = (await (0, builder_util_1.exec)(path7za, ["l", file])).trim();
|
|
// after adding blockmap data will be "Warnings: 1" in the end of output
|
|
const match = /(\d+)\s+\d+\s+\d+\s+files/.exec(archiveInfo);
|
|
if (match == null) {
|
|
builder_util_1.log.warn({ output: archiveInfo }, "cannot compute size of app package");
|
|
}
|
|
else {
|
|
estimatedSize += parseInt(match[1], 10);
|
|
}
|
|
}));
|
|
}
|
|
this.configureDefinesForAllTypeOfInstaller(defines);
|
|
if (isPortable) {
|
|
const { unpackDirName, requestExecutionLevel, splashImage } = options;
|
|
defines.REQUEST_EXECUTION_LEVEL = requestExecutionLevel || "user";
|
|
// https://github.com/electron-userland/electron-builder/issues/5764
|
|
if (typeof unpackDirName === "string" || !unpackDirName) {
|
|
defines.UNPACK_DIR_NAME = unpackDirName || (await (0, builder_util_1.executeAppBuilder)(["ksuid"]));
|
|
}
|
|
if (splashImage != null) {
|
|
defines.SPLASH_IMAGE = path.resolve(packager.projectDir, splashImage);
|
|
}
|
|
}
|
|
else {
|
|
await this.configureDefines(oneClick, defines);
|
|
}
|
|
if (estimatedSize !== 0) {
|
|
// in kb
|
|
defines.ESTIMATED_SIZE = Math.round(estimatedSize / 1024);
|
|
}
|
|
if (packager.compression === "store") {
|
|
commands.SetCompress = "off";
|
|
}
|
|
else {
|
|
// difference - 33.540 vs 33.601, only 61 KB (but zip is faster to decompress)
|
|
// do not use /SOLID - "With solid compression, files are uncompressed to temporary file before they are copied to their final destination",
|
|
// it is not good for portable installer (where built-in NSIS compression is used). http://forums.winamp.com/showpost.php?p=2982902&postcount=6
|
|
commands.SetCompressor = "zlib";
|
|
if (!this.isWebInstaller) {
|
|
defines.COMPRESS = "auto";
|
|
}
|
|
}
|
|
debug(defines);
|
|
debug(commands);
|
|
if (packager.packagerOptions.effectiveOptionComputed != null && (await packager.packagerOptions.effectiveOptionComputed([defines, commands]))) {
|
|
return;
|
|
}
|
|
// prepare short-version variants of defines and commands, to make an uninstaller that doesn't differ much from the previous one
|
|
const definesUninstaller = { ...defines };
|
|
const commandsUninstaller = { ...commands };
|
|
if (appInfo.shortVersion != null) {
|
|
definesUninstaller.VERSION = appInfo.shortVersion;
|
|
commandsUninstaller.VIProductVersion = appInfo.shortVersionWindows;
|
|
commandsUninstaller.VIAddVersionKey = this.computeVersionKey(true);
|
|
}
|
|
const sharedHeader = await this.computeCommonInstallerScriptHeader();
|
|
const script = isPortable
|
|
? await (0, fs_extra_1.readFile)(path.join(nsisUtil_1.nsisTemplatesDir, "portable.nsi"), "utf8")
|
|
: await this.computeScriptAndSignUninstaller(definesUninstaller, commandsUninstaller, installerPath, sharedHeader, archs);
|
|
// copy outfile name into main options, as the computeScriptAndSignUninstaller function was kind enough to add important data to temporary defines.
|
|
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(primaryArch, defaultArch));
|
|
let updateInfo;
|
|
if (this.isWebInstaller) {
|
|
updateInfo = (0, differentialUpdateInfoBuilder_1.createNsisWebDifferentialUpdateInfo)(installerPath, packageFiles);
|
|
}
|
|
else if (this.isBuildDifferentialAware) {
|
|
updateInfo = await (0, differentialUpdateInfoBuilder_1.createBlockmap)(installerPath, this, packager, safeArtifactName);
|
|
}
|
|
if (updateInfo != null && isPerMachine && (oneClick || options.packElevateHelper)) {
|
|
updateInfo.isAdminRightsRequired = true;
|
|
}
|
|
await packager.info.emitArtifactBuildCompleted({
|
|
file: installerPath,
|
|
updateInfo,
|
|
target: this,
|
|
packager,
|
|
arch: primaryArch,
|
|
safeArtifactName,
|
|
isWriteUpdateInfo: !this.isPortable,
|
|
});
|
|
}
|
|
generateGitHubInstallerName(primaryArch, defaultArch) {
|
|
const appInfo = this.packager.appInfo;
|
|
const classifier = appInfo.name.toLowerCase() === appInfo.name ? "setup-" : "Setup-";
|
|
const archSuffix = !this.shouldBuildUniversalInstaller && 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;
|
|
}
|
|
get isWebInstaller() {
|
|
return false;
|
|
}
|
|
async computeScriptAndSignUninstaller(defines, commands, installerPath, sharedHeader, archs) {
|
|
const packager = this.packager;
|
|
const customScriptPath = await packager.getResource(this.options.script, "installer.nsi");
|
|
const script = await (0, fs_extra_1.readFile)(customScriptPath || path.join(nsisUtil_1.nsisTemplatesDir, "installer.nsi"), "utf8");
|
|
if (customScriptPath != null) {
|
|
builder_util_1.log.info({ reason: "custom NSIS script is used" }, "uninstaller is not signed by electron-builder");
|
|
return script;
|
|
}
|
|
// https://github.com/electron-userland/electron-builder/issues/2103
|
|
// it is more safe and reliable to write uninstaller to our out dir
|
|
const uninstallerPath = path.join(this.outDir, `__uninstaller-${this.name}-${this.packager.appInfo.sanitizedName}.exe`);
|
|
const isWin = process.platform === "win32";
|
|
defines.BUILD_UNINSTALLER = null;
|
|
defines.UNINSTALLER_OUT_FILE = isWin ? uninstallerPath : path.win32.join("Z:", uninstallerPath);
|
|
await this.executeMakensis(defines, commands, sharedHeader + (await this.computeFinalScript(script, false, archs)));
|
|
// http://forums.winamp.com/showthread.php?p=3078545
|
|
if ((0, macosVersion_1.isMacOsCatalina)()) {
|
|
try {
|
|
await nsisUtil_1.UninstallerReader.exec(installerPath, uninstallerPath);
|
|
}
|
|
catch (error) {
|
|
builder_util_1.log.warn(`packager.vm is used: ${error.message}`);
|
|
const vm = await packager.vm.value;
|
|
await vm.exec(installerPath, []);
|
|
// Parallels VM can exit after command execution, but NSIS continue to be running
|
|
let i = 0;
|
|
while (!(await (0, builder_util_1.exists)(uninstallerPath)) && i++ < 100) {
|
|
// noinspection JSUnusedLocalSymbols
|
|
await new Promise((resolve, _reject) => setTimeout(resolve, 300));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
await (0, wine_1.execWine)(installerPath, null, [], { env: { __COMPAT_LAYER: "RunAsInvoker" } });
|
|
}
|
|
await packager.sign(uninstallerPath);
|
|
delete defines.BUILD_UNINSTALLER;
|
|
// platform-specific path, not wine
|
|
defines.UNINSTALLER_OUT_FILE = uninstallerPath;
|
|
return script;
|
|
}
|
|
computeVersionKey(short = false) {
|
|
// Error: invalid VIProductVersion format, should be X.X.X.X
|
|
// so, we must strip beta
|
|
const localeId = this.options.language || "1033";
|
|
const appInfo = this.packager.appInfo;
|
|
const versionKey = [
|
|
`/LANG=${localeId} ProductName "${appInfo.productName}"`,
|
|
`/LANG=${localeId} ProductVersion "${appInfo.version}"`,
|
|
`/LANG=${localeId} LegalCopyright "${appInfo.copyright}"`,
|
|
`/LANG=${localeId} FileDescription "${appInfo.description}"`,
|
|
`/LANG=${localeId} FileVersion "${appInfo.buildVersion}"`,
|
|
];
|
|
if (short) {
|
|
versionKey[1] = `/LANG=${localeId} ProductVersion "${appInfo.shortVersion}"`;
|
|
versionKey[4] = `/LANG=${localeId} FileVersion "${appInfo.shortVersion}"`;
|
|
}
|
|
(0, builder_util_1.use)(this.packager.platformSpecificBuildOptions.legalTrademarks, it => versionKey.push(`/LANG=${localeId} LegalTrademarks "${it}"`));
|
|
(0, builder_util_1.use)(appInfo.companyName, it => versionKey.push(`/LANG=${localeId} CompanyName "${it}"`));
|
|
return versionKey;
|
|
}
|
|
configureDefines(oneClick, defines) {
|
|
const packager = this.packager;
|
|
const options = this.options;
|
|
const asyncTaskManager = new builder_util_1.AsyncTaskManager(packager.info.cancellationToken);
|
|
if (oneClick) {
|
|
defines.ONE_CLICK = null;
|
|
if (options.runAfterFinish !== false) {
|
|
defines.RUN_AFTER_FINISH = null;
|
|
}
|
|
asyncTaskManager.add(async () => {
|
|
const installerHeaderIcon = await packager.getResource(options.installerHeaderIcon, "installerHeaderIcon.ico");
|
|
if (installerHeaderIcon != null) {
|
|
defines.HEADER_ICO = installerHeaderIcon;
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
if (options.runAfterFinish === false) {
|
|
defines.HIDE_RUN_AFTER_FINISH = null;
|
|
}
|
|
asyncTaskManager.add(async () => {
|
|
const installerHeader = await packager.getResource(options.installerHeader, "installerHeader.bmp");
|
|
if (installerHeader != null) {
|
|
defines.MUI_HEADERIMAGE = null;
|
|
defines.MUI_HEADERIMAGE_RIGHT = null;
|
|
defines.MUI_HEADERIMAGE_BITMAP = installerHeader;
|
|
}
|
|
});
|
|
asyncTaskManager.add(async () => {
|
|
const bitmap = (await packager.getResource(options.installerSidebar, "installerSidebar.bmp")) || "${NSISDIR}\\Contrib\\Graphics\\Wizard\\nsis3-metro.bmp";
|
|
defines.MUI_WELCOMEFINISHPAGE_BITMAP = bitmap;
|
|
defines.MUI_UNWELCOMEFINISHPAGE_BITMAP = (await packager.getResource(options.uninstallerSidebar, "uninstallerSidebar.bmp")) || bitmap;
|
|
});
|
|
if (options.allowElevation !== false) {
|
|
defines.MULTIUSER_INSTALLMODE_ALLOW_ELEVATION = null;
|
|
}
|
|
}
|
|
if (options.perMachine === true) {
|
|
defines.INSTALL_MODE_PER_ALL_USERS = null;
|
|
}
|
|
if (options.selectPerMachineByDefault === true) {
|
|
defines.INSTALL_MODE_PER_ALL_USERS_DEFAULT = null;
|
|
}
|
|
if (!oneClick || options.perMachine === true) {
|
|
defines.INSTALL_MODE_PER_ALL_USERS_REQUIRED = null;
|
|
}
|
|
if (options.allowToChangeInstallationDirectory) {
|
|
if (oneClick) {
|
|
throw new builder_util_1.InvalidConfigurationError("allowToChangeInstallationDirectory makes sense only for assisted installer (please set oneClick to false)");
|
|
}
|
|
defines.allowToChangeInstallationDirectory = null;
|
|
}
|
|
if (options.removeDefaultUninstallWelcomePage) {
|
|
defines.removeDefaultUninstallWelcomePage = null;
|
|
}
|
|
const commonOptions = (0, CommonWindowsInstallerConfiguration_1.getEffectiveOptions)(options, packager);
|
|
if (commonOptions.menuCategory != null) {
|
|
defines.MENU_FILENAME = commonOptions.menuCategory;
|
|
}
|
|
defines.SHORTCUT_NAME = commonOptions.shortcutName;
|
|
if (options.deleteAppDataOnUninstall) {
|
|
defines.DELETE_APP_DATA_ON_UNINSTALL = null;
|
|
}
|
|
asyncTaskManager.add(async () => {
|
|
const uninstallerIcon = await packager.getResource(options.uninstallerIcon, "uninstallerIcon.ico");
|
|
if (uninstallerIcon != null) {
|
|
// we don't need to copy MUI_UNICON (defaults to app icon), so, we have 2 defines
|
|
defines.UNINSTALLER_ICON = uninstallerIcon;
|
|
defines.MUI_UNICON = uninstallerIcon;
|
|
}
|
|
});
|
|
defines.UNINSTALL_DISPLAY_NAME = packager.expandMacro(options.uninstallDisplayName || "${productName} ${version}", null, {}, false);
|
|
if (commonOptions.isCreateDesktopShortcut === CommonWindowsInstallerConfiguration_1.DesktopShortcutCreationPolicy.NEVER) {
|
|
defines.DO_NOT_CREATE_DESKTOP_SHORTCUT = null;
|
|
}
|
|
if (commonOptions.isCreateDesktopShortcut === CommonWindowsInstallerConfiguration_1.DesktopShortcutCreationPolicy.ALWAYS) {
|
|
defines.RECREATE_DESKTOP_SHORTCUT = null;
|
|
}
|
|
if (!commonOptions.isCreateStartMenuShortcut) {
|
|
defines.DO_NOT_CREATE_START_MENU_SHORTCUT = null;
|
|
}
|
|
if (options.displayLanguageSelector === true) {
|
|
defines.DISPLAY_LANG_SELECTOR = null;
|
|
}
|
|
return asyncTaskManager.awaitTasks();
|
|
}
|
|
configureDefinesForAllTypeOfInstaller(defines) {
|
|
const appInfo = this.packager.appInfo;
|
|
const companyName = appInfo.companyName;
|
|
if (companyName != null) {
|
|
defines.COMPANY_NAME = companyName;
|
|
}
|
|
// electron uses product file name as app data, define it as well to remove on uninstall
|
|
if (defines.APP_FILENAME !== appInfo.productFilename) {
|
|
defines.APP_PRODUCT_FILENAME = appInfo.productFilename;
|
|
}
|
|
if (this.isWebInstaller) {
|
|
defines.APP_PACKAGE_STORE_FILE = `${appInfo.updaterCacheDirName}\\${builder_util_runtime_1.CURRENT_APP_PACKAGE_FILE_NAME}`;
|
|
}
|
|
else {
|
|
defines.APP_INSTALLER_STORE_FILE = `${appInfo.updaterCacheDirName}\\${builder_util_runtime_1.CURRENT_APP_INSTALLER_FILE_NAME}`;
|
|
}
|
|
if (!this.isWebInstaller && defines.APP_BUILD_DIR == null) {
|
|
const options = this.options;
|
|
if (options.useZip) {
|
|
defines.ZIP_COMPRESSION = null;
|
|
}
|
|
defines.COMPRESSION_METHOD = options.useZip ? "zip" : "7z";
|
|
}
|
|
}
|
|
async executeMakensis(defines, commands, script) {
|
|
const args = this.options.warningsAsErrors === false ? [] : ["-WX"];
|
|
args.push("-INPUTCHARSET", "UTF8");
|
|
for (const name of Object.keys(defines)) {
|
|
const value = defines[name];
|
|
if (value == null) {
|
|
args.push(`-D${name}`);
|
|
}
|
|
else {
|
|
args.push(`-D${name}=${value}`);
|
|
}
|
|
}
|
|
for (const name of Object.keys(commands)) {
|
|
const value = commands[name];
|
|
if (Array.isArray(value)) {
|
|
for (const c of value) {
|
|
args.push(`-X${name} ${c}`);
|
|
}
|
|
}
|
|
else {
|
|
args.push(`-X${name} ${value}`);
|
|
}
|
|
}
|
|
args.push("-");
|
|
if (this.packager.debugLogger.isEnabled) {
|
|
this.packager.debugLogger.add("nsis.script", script);
|
|
}
|
|
const nsisPath = await (0, nsisUtil_1.NSIS_PATH)();
|
|
const command = path.join(nsisPath, process.platform === "darwin" ? "mac" : process.platform === "win32" ? "Bin" : "linux", process.platform === "win32" ? "makensis.exe" : "makensis");
|
|
// if (process.platform === "win32") {
|
|
// fix for an issue caused by virus scanners, locking the file during write
|
|
// https://github.com/electron-userland/electron-builder/issues/5005
|
|
await ensureNotBusy(commands["OutFile"].replace(/"/g, ""));
|
|
// }
|
|
await (0, builder_util_1.spawnAndWrite)(command, args, script, {
|
|
// we use NSIS_CONFIG_CONST_DATA_PATH=no to build makensis on Linux, but in any case it doesn't use stubs as MacOS/Windows version, so, we explicitly set NSISDIR
|
|
env: { ...process.env, NSISDIR: nsisPath },
|
|
cwd: nsisUtil_1.nsisTemplatesDir,
|
|
});
|
|
}
|
|
async computeCommonInstallerScriptHeader() {
|
|
const packager = this.packager;
|
|
const options = this.options;
|
|
const scriptGenerator = new nsisScriptGenerator_1.NsisScriptGenerator();
|
|
const langConfigurator = new nsisLang_1.LangConfigurator(options);
|
|
scriptGenerator.include(path.join(nsisUtil_1.nsisTemplatesDir, "include", "StdUtils.nsh"));
|
|
const includeDir = path.join(nsisUtil_1.nsisTemplatesDir, "include");
|
|
scriptGenerator.addIncludeDir(includeDir);
|
|
scriptGenerator.flags(["updated", "force-run", "keep-shortcuts", "no-desktop-shortcut", "delete-app-data", "allusers", "currentuser"]);
|
|
(0, nsisLang_1.createAddLangsMacro)(scriptGenerator, langConfigurator);
|
|
const taskManager = new builder_util_1.AsyncTaskManager(packager.info.cancellationToken);
|
|
const pluginArch = this.isUnicodeEnabled ? "x86-unicode" : "x86-ansi";
|
|
taskManager.add(async () => {
|
|
scriptGenerator.addPluginDir(pluginArch, path.join(await nsisResourcePathPromise(), "plugins", pluginArch));
|
|
});
|
|
taskManager.add(async () => {
|
|
const userPluginDir = path.join(packager.info.buildResourcesDir, pluginArch);
|
|
const stat = await (0, builder_util_1.statOrNull)(userPluginDir);
|
|
if (stat != null && stat.isDirectory()) {
|
|
scriptGenerator.addPluginDir(pluginArch, userPluginDir);
|
|
}
|
|
});
|
|
taskManager.addTask((0, nsisLang_1.addCustomMessageFileInclude)("messages.yml", packager, scriptGenerator, langConfigurator));
|
|
if (!this.isPortable) {
|
|
if (options.oneClick === false) {
|
|
taskManager.addTask((0, nsisLang_1.addCustomMessageFileInclude)("assistedMessages.yml", packager, scriptGenerator, langConfigurator));
|
|
}
|
|
taskManager.add(async () => {
|
|
const customInclude = await packager.getResource(this.options.include, "installer.nsh");
|
|
if (customInclude != null) {
|
|
scriptGenerator.addIncludeDir(packager.info.buildResourcesDir);
|
|
scriptGenerator.include(customInclude);
|
|
}
|
|
});
|
|
}
|
|
await taskManager.awaitTasks();
|
|
return scriptGenerator.build();
|
|
}
|
|
async computeFinalScript(originalScript, isInstaller, archs) {
|
|
const packager = this.packager;
|
|
const options = this.options;
|
|
const langConfigurator = new nsisLang_1.LangConfigurator(options);
|
|
const scriptGenerator = new nsisScriptGenerator_1.NsisScriptGenerator();
|
|
const taskManager = new builder_util_1.AsyncTaskManager(packager.info.cancellationToken);
|
|
if (isInstaller) {
|
|
// http://stackoverflow.com/questions/997456/nsis-license-file-based-on-language-selection
|
|
taskManager.add(() => (0, nsisLicense_1.computeLicensePage)(packager, options, scriptGenerator, langConfigurator.langs));
|
|
}
|
|
await taskManager.awaitTasks();
|
|
if (this.isPortable) {
|
|
return scriptGenerator.build() + originalScript;
|
|
}
|
|
const preCompressedFileExtensions = this.getPreCompressedFileExtensions();
|
|
if (preCompressedFileExtensions != null && preCompressedFileExtensions.length !== 0) {
|
|
for (const [arch, dir] of archs.entries()) {
|
|
await generateForPreCompressed(preCompressedFileExtensions, dir, arch, scriptGenerator);
|
|
}
|
|
}
|
|
const fileAssociations = packager.fileAssociations;
|
|
if (fileAssociations.length !== 0) {
|
|
scriptGenerator.include(path.join(path.join(nsisUtil_1.nsisTemplatesDir, "include"), "FileAssociation.nsh"));
|
|
if (isInstaller) {
|
|
const registerFileAssociationsScript = new nsisScriptGenerator_1.NsisScriptGenerator();
|
|
for (const item of fileAssociations) {
|
|
const extensions = (0, builder_util_1.asArray)(item.ext).map(platformPackager_1.normalizeExt);
|
|
for (const ext of extensions) {
|
|
const customIcon = await packager.getResource((0, builder_util_1.getPlatformIconFileName)(item.icon, false), `${extensions[0]}.ico`);
|
|
let installedIconPath = "$appExe,0";
|
|
if (customIcon != null) {
|
|
installedIconPath = `$INSTDIR\\resources\\${path.basename(customIcon)}`;
|
|
registerFileAssociationsScript.file(installedIconPath, customIcon);
|
|
}
|
|
const icon = `"${installedIconPath}"`;
|
|
const commandText = `"Open with ${packager.appInfo.productName}"`;
|
|
const command = '"$appExe $\\"%1$\\""';
|
|
registerFileAssociationsScript.insertMacro("APP_ASSOCIATE", `"${ext}" "${item.name || ext}" "${item.description || ""}" ${icon} ${commandText} ${command}`);
|
|
}
|
|
}
|
|
scriptGenerator.macro("registerFileAssociations", registerFileAssociationsScript);
|
|
}
|
|
else {
|
|
const unregisterFileAssociationsScript = new nsisScriptGenerator_1.NsisScriptGenerator();
|
|
for (const item of fileAssociations) {
|
|
for (const ext of (0, builder_util_1.asArray)(item.ext)) {
|
|
unregisterFileAssociationsScript.insertMacro("APP_UNASSOCIATE", `"${(0, platformPackager_1.normalizeExt)(ext)}" "${item.name || ext}"`);
|
|
}
|
|
}
|
|
scriptGenerator.macro("unregisterFileAssociations", unregisterFileAssociationsScript);
|
|
}
|
|
}
|
|
return scriptGenerator.build() + originalScript;
|
|
}
|
|
}
|
|
exports.NsisTarget = NsisTarget;
|
|
async function generateForPreCompressed(preCompressedFileExtensions, dir, arch, scriptGenerator) {
|
|
const resourcesDir = path.join(dir, "resources");
|
|
const dirInfo = await (0, builder_util_1.statOrNull)(resourcesDir);
|
|
if (dirInfo == null || !dirInfo.isDirectory()) {
|
|
return;
|
|
}
|
|
const nodeModules = `${path.sep}node_modules`;
|
|
const preCompressedAssets = await (0, builder_util_1.walk)(resourcesDir, (file, stat) => {
|
|
if (stat.isDirectory()) {
|
|
return !file.endsWith(nodeModules);
|
|
}
|
|
else {
|
|
return preCompressedFileExtensions.some(it => file.endsWith(it));
|
|
}
|
|
});
|
|
if (preCompressedAssets.length !== 0) {
|
|
const macro = new nsisScriptGenerator_1.NsisScriptGenerator();
|
|
for (const file of preCompressedAssets) {
|
|
macro.file(`$INSTDIR\\${path.relative(dir, file).replace(/\//g, "\\")}`, file);
|
|
}
|
|
scriptGenerator.macro(`customFiles_${builder_util_1.Arch[arch]}`, macro);
|
|
}
|
|
}
|
|
async function ensureNotBusy(outFile) {
|
|
function isBusy(wasBusyBefore) {
|
|
return new Promise((resolve, reject) => {
|
|
fs.open(outFile, "r+", (error, fd) => {
|
|
try {
|
|
if (error != null && error.code === "EBUSY") {
|
|
if (!wasBusyBefore) {
|
|
builder_util_1.log.info({}, "output file is locked for writing (maybe by virus scanner) => waiting for unlock...");
|
|
}
|
|
resolve(false);
|
|
}
|
|
else if (fd == null) {
|
|
resolve(true);
|
|
}
|
|
else {
|
|
fs.close(fd, () => resolve(true));
|
|
}
|
|
}
|
|
catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}).then(result => {
|
|
if (result) {
|
|
return true;
|
|
}
|
|
else {
|
|
return new Promise(resolve => setTimeout(resolve, 2000)).then(() => isBusy(true));
|
|
}
|
|
});
|
|
}
|
|
await isBusy(false);
|
|
}
|
|
async function createPackageFileInfo(file) {
|
|
return {
|
|
path: file,
|
|
size: (await (0, fs_extra_1.stat)(file)).size,
|
|
sha512: await (0, hash_1.hashFile)(file),
|
|
};
|
|
}
|
|
//# sourceMappingURL=NsisTarget.js.map
|