From cbcab5189502b9ed9b8504de7fb252205f6c9be6 Mon Sep 17 00:00:00 2001 From: Nikolay Velizhanin Date: Mon, 10 Apr 2023 18:00:36 +0500 Subject: [PATCH 1/5] feat(nsis): display required disk space #7487 --- .../src/targets/nsis/Defines.ts | 4 + .../src/targets/nsis/NsisTarget.ts | 6 +- .../src/targets/nsis/nsisUtil.ts | 32 +++++-- .../app-builder-lib/templates/nsis/common.nsh | 29 ++++++- .../templates/nsis/installer.nsi | 84 ++++++++++--------- packages/builder-util/src/fs.ts | 19 +++++ 6 files changed, 121 insertions(+), 53 deletions(-) diff --git a/packages/app-builder-lib/src/targets/nsis/Defines.ts b/packages/app-builder-lib/src/targets/nsis/Defines.ts index 07f78c3b7c0..5402f35b28e 100644 --- a/packages/app-builder-lib/src/targets/nsis/Defines.ts +++ b/packages/app-builder-lib/src/targets/nsis/Defines.ts @@ -44,6 +44,10 @@ export type Defines = { APP_ARM64_HASH?: string APP_32_HASH?: string + APP_64_UNPACKED_SIZE?: string + APP_ARM64_UNPACKED_SIZE?: string + APP_32_UNPACKED_SIZE?: string + REQUEST_EXECUTION_LEVEL?: PortableOptions["requestExecutionLevel"] UNPACK_DIR_NAME?: string | false diff --git a/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts b/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts index 6a9ecab56c1..706bbee6ed8 100644 --- a/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts +++ b/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts @@ -229,7 +229,7 @@ export class NsisTarget extends Target { defines.APP_BUILD_DIR = archs.get(archs.keys().next().value) } else { await BluebirdPromise.map(archs.keys(), async arch => { - const fileInfo = await this.packageHelper.packArch(arch, this) + const { fileInfo, unpackedSize } = await this.packageHelper.packArch(arch, this) const file = fileInfo.path const defineKey = arch === Arch.x64 ? "APP_64" : arch === Arch.arm64 ? "APP_ARM64" : "APP_32" defines[defineKey] = file @@ -240,6 +240,10 @@ export class NsisTarget extends Target { // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion const defineHashKey = `${defineKey}_HASH` as "APP_64_HASH" | "APP_ARM64_HASH" | "APP_32_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` as "APP_64_UNPACKED_SIZE" | "APP_ARM64_UNPACKED_SIZE" | "APP_32_UNPACKED_SIZE" + defines[defineUnpackedSizeKey] = Math.ceil(unpackedSize / 1024).toString() if (this.isWebInstaller) { await packager.dispatchArtifactCreated(file, this, arch) diff --git a/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts b/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts index 4805f2aae2d..c1bce381016 100644 --- a/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts +++ b/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts @@ -1,7 +1,7 @@ import { Arch, log } from "builder-util" import { PackageFileInfo } from "builder-util-runtime" import { getBinFromUrl, getBinFromCustomLoc } from "../../binDownload" -import { copyFile } from "builder-util/out/fs" +import { copyFile, dirSize } from "builder-util/out/fs" import * as path from "path" import { getTemplatePath } from "../../util/pathManager" import { NsisTarget } from "./NsisTarget" @@ -39,8 +39,13 @@ export const NSIS_PATH = () => { }) } +export interface PackArchResult { + fileInfo: PackageFileInfo + unpackedSize: number +} + export class AppPackageHelper { - private readonly archToFileInfo = new Map>() + private readonly archToResult = new Map>() private readonly infoToIsDelete = new Map() /** @private */ @@ -48,21 +53,30 @@ export class AppPackageHelper { constructor(private readonly elevateHelper: CopyElevateHelper) {} - async packArch(arch: Arch, target: NsisTarget): Promise { - let infoPromise = this.archToFileInfo.get(arch) - if (infoPromise == null) { + async packArch(arch: Arch, target: NsisTarget): Promise { + let resultPromise = this.archToResult.get(arch) + if (resultPromise == null) { const appOutDir = target.archs.get(arch)! - infoPromise = this.elevateHelper.copy(appOutDir, target).then(() => target.buildAppPackage(appOutDir, arch)) - this.archToFileInfo.set(arch, infoPromise) + resultPromise = this.elevateHelper + .copy(appOutDir, target) + .then(() => dirSize(appOutDir)) + .then(unpackedSize => + target.buildAppPackage(appOutDir, arch).then(fileInfo => ({ + fileInfo, + unpackedSize, + })) + ) + this.archToResult.set(arch, resultPromise) } - const info = await infoPromise + const result = await resultPromise + const { fileInfo: info } = result if (target.isWebInstaller) { this.infoToIsDelete.set(info, false) } else if (!this.infoToIsDelete.has(info)) { this.infoToIsDelete.set(info, true) } - return info + return result } async finishBuild(): Promise { diff --git a/packages/app-builder-lib/templates/nsis/common.nsh b/packages/app-builder-lib/templates/nsis/common.nsh index 0509c033876..821e083e9aa 100644 --- a/packages/app-builder-lib/templates/nsis/common.nsh +++ b/packages/app-builder-lib/templates/nsis/common.nsh @@ -3,7 +3,6 @@ BrandingText "${PRODUCT_NAME} ${VERSION}" ShowInstDetails nevershow -SpaceTexts none !ifdef BUILD_UNINSTALLER ShowUninstDetails nevershow !endif @@ -13,6 +12,32 @@ Name "${PRODUCT_NAME}" !define APP_EXECUTABLE_FILENAME "${PRODUCT_FILENAME}.exe" !define UNINSTALL_FILENAME "Uninstall ${PRODUCT_FILENAME}.exe" +!macro setSpaceRequired SECTION_ID + !ifdef APP_64_UNPACKED_SIZE + !ifdef APP_32_UNPACKED_SIZE + !ifdef APP_ARM64_UNPACKED_SIZE + ${if} ${IsNativeARM64} + SectionSetSize ${SECTION_ID} ${APP_ARM64_UNPACKED_SIZE} + ${elseif} ${IsNativeAMD64} + SectionSetSize ${SECTION_ID} ${APP_64_UNPACKED_SIZE} + ${else} + SectionSetSize ${SECTION_ID} ${APP_32_UNPACKED_SIZE} + ${endif} + !else + ${if} ${RunningX64} + SectionSetSize ${SECTION_ID} ${APP_64_UNPACKED_SIZE} + ${else} + SectionSetSize ${SECTION_ID} ${APP_32_UNPACKED_SIZE} + ${endif} + !endif + !else + SectionSetSize ${SECTION_ID} ${APP_64_UNPACKED_SIZE} + !endif + !else + SectionSetSize ${SECTION_ID} ${APP_32_UNPACKED_SIZE} + !endif +!macroend + !macro check64BitAndSetRegView # https://github.com/electron-userland/electron-builder/issues/2420 ${If} ${IsWin2000} @@ -107,7 +132,7 @@ Name "${PRODUCT_NAME}" LogSet ${SETTING} !endif !macroend - + !define LogText "!insertmacro LogTextMacroEB" !macro LogTextMacroEB INPUT_TEXT !ifdef ENABLE_LOGGING_ELECTRON_BUILDER diff --git a/packages/app-builder-lib/templates/nsis/installer.nsi b/packages/app-builder-lib/templates/nsis/installer.nsi index 4c69e9a888a..7413a92909b 100644 --- a/packages/app-builder-lib/templates/nsis/installer.nsi +++ b/packages/app-builder-lib/templates/nsis/installer.nsi @@ -39,50 +39,11 @@ Var oldMenuDirectory !insertmacro customHeader !endif -Function .onInit - SetOutPath $INSTDIR - ${LogSet} on - - !ifmacrodef preInit - !insertmacro preInit - !endif - - !ifdef DISPLAY_LANG_SELECTOR - !insertmacro MUI_LANGDLL_DISPLAY - !endif - - !ifdef BUILD_UNINSTALLER - WriteUninstaller "${UNINSTALLER_OUT_FILE}" - !insertmacro quitSuccess - !else - !insertmacro check64BitAndSetRegView - - !ifdef ONE_CLICK - !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTANCE - !else - ${IfNot} ${UAC_IsInnerInstance} - !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTANCE - ${EndIf} - !endif - - !insertmacro initMultiUser - - !ifmacrodef customInit - !insertmacro customInit - !endif - - !ifmacrodef addLicenseFiles - InitPluginsDir - !insertmacro addLicenseFiles - !endif - !endif -FunctionEnd - !ifndef BUILD_UNINSTALLER !include "installUtil.nsh" !endif -Section "install" +Section "install" INSTALL_SECTION_ID !ifndef BUILD_UNINSTALLER # If we're running a silent upgrade of a per-machine installation, elevate so extracting the new app will succeed. # For a non-silent install, the elevation will be triggered when the install mode is selected in the UI, @@ -116,4 +77,45 @@ SectionEnd !ifdef BUILD_UNINSTALLER !include "uninstaller.nsh" -!endif \ No newline at end of file +!endif + +Function .onInit + !insertmacro setSpaceRequired ${INSTALL_SECTION_ID} + + SetOutPath $INSTDIR + ${LogSet} on + + !ifmacrodef preInit + !insertmacro preInit + !endif + + !ifdef DISPLAY_LANG_SELECTOR + !insertmacro MUI_LANGDLL_DISPLAY + !endif + + !ifdef BUILD_UNINSTALLER + WriteUninstaller "${UNINSTALLER_OUT_FILE}" + !insertmacro quitSuccess + !else + !insertmacro check64BitAndSetRegView + + !ifdef ONE_CLICK + !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTANCE + !else + ${IfNot} ${UAC_IsInnerInstance} + !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTANCE + ${EndIf} + !endif + + !insertmacro initMultiUser + + !ifmacrodef customInit + !insertmacro customInit + !endif + + !ifmacrodef addLicenseFiles + InitPluginsDir + !insertmacro addLicenseFiles + !endif + !endif +FunctionEnd \ No newline at end of file diff --git a/packages/builder-util/src/fs.ts b/packages/builder-util/src/fs.ts index a5719fac93c..218a5226f87 100644 --- a/packages/builder-util/src/fs.ts +++ b/packages/builder-util/src/fs.ts @@ -306,6 +306,25 @@ export function copyDir(src: string, destination: string, options: CopyDirOption }).then(() => BluebirdPromise.map(links, it => symlink(it.link, it.file, symlinkType), CONCURRENCY)) } +export async function dirSize(dirPath: string): Promise { + const entries = await readdir(dirPath, { withFileTypes: true }) + + const entrySizes = entries.map(async entry => { + const entryPath = path.join(dirPath, entry.name) + + if (entry.isDirectory()) return await dirSize(entryPath) + + if (entry.isFile()) { + const { size } = await stat(entryPath) + return size + } + + return 0 + }) + + return (await Promise.all(entrySizes)).reduce((entrySize, totalSize) => entrySize + totalSize, 0) +} + // eslint-disable-next-line @typescript-eslint/no-unused-vars export const DO_NOT_USE_HARD_LINKS = (file: string) => false // eslint-disable-next-line @typescript-eslint/no-unused-vars From c830d33f36dc47d50da9dd7e4e4a93992ecc7b52 Mon Sep 17 00:00:00 2001 From: Nikolay Velizhanin Date: Thu, 13 Apr 2023 12:30:03 +0500 Subject: [PATCH 2/5] feat(nsis): display required disk space #7487 --- packages/app-builder-lib/templates/nsis/common.nsh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/app-builder-lib/templates/nsis/common.nsh b/packages/app-builder-lib/templates/nsis/common.nsh index 821e083e9aa..2aab0c3ffee 100644 --- a/packages/app-builder-lib/templates/nsis/common.nsh +++ b/packages/app-builder-lib/templates/nsis/common.nsh @@ -34,7 +34,9 @@ Name "${PRODUCT_NAME}" SectionSetSize ${SECTION_ID} ${APP_64_UNPACKED_SIZE} !endif !else - SectionSetSize ${SECTION_ID} ${APP_32_UNPACKED_SIZE} + !ifdef APP_32_UNPACKED_SIZE + SectionSetSize ${SECTION_ID} ${APP_32_UNPACKED_SIZE} + !endif !endif !macroend From da837a82567dce6c9ccd0a99392ab19fae1de92a Mon Sep 17 00:00:00 2001 From: Nikolay Velizhanin Date: Mon, 17 Apr 2023 13:35:49 +0500 Subject: [PATCH 3/5] feat(nsis): display required disk space Adjusted code style, changed operations order in packArch #7487 --- .../app-builder-lib/src/targets/nsis/nsisUtil.ts | 12 +++++------- packages/builder-util/src/fs.ts | 4 +++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts b/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts index c1bce381016..d3c9617c6b8 100644 --- a/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts +++ b/packages/app-builder-lib/src/targets/nsis/nsisUtil.ts @@ -59,13 +59,11 @@ export class AppPackageHelper { const appOutDir = target.archs.get(arch)! resultPromise = this.elevateHelper .copy(appOutDir, target) - .then(() => dirSize(appOutDir)) - .then(unpackedSize => - target.buildAppPackage(appOutDir, arch).then(fileInfo => ({ - fileInfo, - unpackedSize, - })) - ) + .then(() => target.buildAppPackage(appOutDir, arch)) + .then(async fileInfo => ({ + fileInfo, + unpackedSize: await dirSize(appOutDir), + })) this.archToResult.set(arch, resultPromise) } diff --git a/packages/builder-util/src/fs.ts b/packages/builder-util/src/fs.ts index 218a5226f87..0a67b4f63ee 100644 --- a/packages/builder-util/src/fs.ts +++ b/packages/builder-util/src/fs.ts @@ -312,7 +312,9 @@ export async function dirSize(dirPath: string): Promise { const entrySizes = entries.map(async entry => { const entryPath = path.join(dirPath, entry.name) - if (entry.isDirectory()) return await dirSize(entryPath) + if (entry.isDirectory()) { + return await dirSize(entryPath) + } if (entry.isFile()) { const { size } = await stat(entryPath) From 1b9b5299d2d656e586ce740814c1c6a4a2a22e60 Mon Sep 17 00:00:00 2001 From: Nikolay Velizhanin Date: Mon, 17 Apr 2023 13:44:19 +0500 Subject: [PATCH 4/5] feat(nsis): display required disk space Added changeset #7487 --- .changeset/modern-waves-compete.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/modern-waves-compete.md diff --git a/.changeset/modern-waves-compete.md b/.changeset/modern-waves-compete.md new file mode 100644 index 00000000000..23b259457c8 --- /dev/null +++ b/.changeset/modern-waves-compete.md @@ -0,0 +1,6 @@ +--- +"app-builder-lib": minor +"builder-util": minor +--- + +Display "Space required" text for NSIS installer From d1aeceae551e495d7eae656be092ecc3d3e80ca5 Mon Sep 17 00:00:00 2001 From: Nikolay Velizhanin Date: Mon, 17 Apr 2023 18:07:10 +0500 Subject: [PATCH 5/5] feat(nsis): display required disk space Fixed warning 6000 unknown variable/constant "{SYSTYPE_PTR}" detected #7487 --- .../templates/nsis/installer.nsi | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/packages/app-builder-lib/templates/nsis/installer.nsi b/packages/app-builder-lib/templates/nsis/installer.nsi index 7413a92909b..1b88a9d50a6 100644 --- a/packages/app-builder-lib/templates/nsis/installer.nsi +++ b/packages/app-builder-lib/templates/nsis/installer.nsi @@ -39,48 +39,8 @@ Var oldMenuDirectory !insertmacro customHeader !endif -!ifndef BUILD_UNINSTALLER - !include "installUtil.nsh" -!endif - -Section "install" INSTALL_SECTION_ID - !ifndef BUILD_UNINSTALLER - # If we're running a silent upgrade of a per-machine installation, elevate so extracting the new app will succeed. - # For a non-silent install, the elevation will be triggered when the install mode is selected in the UI, - # but that won't be executed when silent. - !ifndef INSTALL_MODE_PER_ALL_USERS - !ifndef ONE_CLICK - ${if} $hasPerMachineInstallation == "1" # set in onInit by initMultiUser - ${andIf} ${Silent} - ${ifNot} ${UAC_IsAdmin} - ShowWindow $HWNDPARENT ${SW_HIDE} - !insertmacro UAC_RunElevated - ${Switch} $0 - ${Case} 0 - ${Break} - ${Case} 1223 ;user aborted - ${Break} - ${Default} - MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Unable to elevate, error $0" - ${Break} - ${EndSwitch} - Quit - ${else} - !insertmacro setInstallModePerAllUsers - ${endIf} - ${endIf} - !endif - !endif - !include "installSection.nsh" - !endif -SectionEnd - -!ifdef BUILD_UNINSTALLER - !include "uninstaller.nsh" -!endif - Function .onInit - !insertmacro setSpaceRequired ${INSTALL_SECTION_ID} + Call setInstallSectionSpaceRequired SetOutPath $INSTDIR ${LogSet} on @@ -118,4 +78,48 @@ Function .onInit !insertmacro addLicenseFiles !endif !endif -FunctionEnd \ No newline at end of file +FunctionEnd + +!ifndef BUILD_UNINSTALLER + !include "installUtil.nsh" +!endif + +Section "install" INSTALL_SECTION_ID + !ifndef BUILD_UNINSTALLER + # If we're running a silent upgrade of a per-machine installation, elevate so extracting the new app will succeed. + # For a non-silent install, the elevation will be triggered when the install mode is selected in the UI, + # but that won't be executed when silent. + !ifndef INSTALL_MODE_PER_ALL_USERS + !ifndef ONE_CLICK + ${if} $hasPerMachineInstallation == "1" # set in onInit by initMultiUser + ${andIf} ${Silent} + ${ifNot} ${UAC_IsAdmin} + ShowWindow $HWNDPARENT ${SW_HIDE} + !insertmacro UAC_RunElevated + ${Switch} $0 + ${Case} 0 + ${Break} + ${Case} 1223 ;user aborted + ${Break} + ${Default} + MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Unable to elevate, error $0" + ${Break} + ${EndSwitch} + Quit + ${else} + !insertmacro setInstallModePerAllUsers + ${endIf} + ${endIf} + !endif + !endif + !include "installSection.nsh" + !endif +SectionEnd + +Function setInstallSectionSpaceRequired + !insertmacro setSpaceRequired ${INSTALL_SECTION_ID} +FunctionEnd + +!ifdef BUILD_UNINSTALLER + !include "uninstaller.nsh" +!endif \ No newline at end of file