Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nsis): display "Space required" text for NSIS installer #7531

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/app-builder-lib/src/targets/nsis/Defines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion packages/app-builder-lib/src/targets/nsis/NsisTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
32 changes: 23 additions & 9 deletions packages/app-builder-lib/src/targets/nsis/nsisUtil.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -39,30 +39,44 @@ export const NSIS_PATH = () => {
})
}

export interface PackArchResult {
fileInfo: PackageFileInfo
unpackedSize: number
}

export class AppPackageHelper {
private readonly archToFileInfo = new Map<Arch, Promise<PackageFileInfo>>()
private readonly archToResult = new Map<Arch, Promise<PackArchResult>>()
private readonly infoToIsDelete = new Map<PackageFileInfo, boolean>()

/** @private */
refCount = 0

constructor(private readonly elevateHelper: CopyElevateHelper) {}

async packArch(arch: Arch, target: NsisTarget): Promise<PackageFileInfo> {
let infoPromise = this.archToFileInfo.get(arch)
if (infoPromise == null) {
async packArch(arch: Arch, target: NsisTarget): Promise<PackArchResult> {
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,
}))
mmaietta marked this conversation as resolved.
Show resolved Hide resolved
)
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<any> {
Expand Down
29 changes: 27 additions & 2 deletions packages/app-builder-lib/templates/nsis/common.nsh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

BrandingText "${PRODUCT_NAME} ${VERSION}"
ShowInstDetails nevershow
SpaceTexts none
!ifdef BUILD_UNINSTALLER
ShowUninstDetails nevershow
!endif
Expand All @@ -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}
Expand Down Expand Up @@ -107,7 +132,7 @@ Name "${PRODUCT_NAME}"
LogSet ${SETTING}
!endif
!macroend

!define LogText "!insertmacro LogTextMacroEB"
!macro LogTextMacroEB INPUT_TEXT
!ifdef ENABLE_LOGGING_ELECTRON_BUILDER
Expand Down
84 changes: 43 additions & 41 deletions packages/app-builder-lib/templates/nsis/installer.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -116,4 +77,45 @@ SectionEnd

!ifdef BUILD_UNINSTALLER
!include "uninstaller.nsh"
!endif
!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
19 changes: 19 additions & 0 deletions packages/builder-util/src/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number> {
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)
mmaietta marked this conversation as resolved.
Show resolved Hide resolved

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
Expand Down