diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml
index c18c70f3813..68739d4e80f 100644
--- a/.idea/dictionaries/develar.xml
+++ b/.idea/dictionaries/develar.xml
@@ -100,6 +100,7 @@
tsconfig
udbz
udro
+ unassociate
unicon
userprofile
valuename
diff --git a/docs/Options.md b/docs/Options.md
index 73cd30c4bb8..3a2da9eedb6 100644
--- a/docs/Options.md
+++ b/docs/Options.md
@@ -167,6 +167,7 @@ NSIS only, [in progress](https://github.com/electron-userland/electron-builder/i
| --- | ---
| **ext** | The extension (minus the leading period). e.g. `png`
| **name** | The name. e.g. `PNG`
+| description | *windows-only.* The description.
## `.directories`
diff --git a/package.json b/package.json
index 7e2df44c451..16baf3314c4 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,7 @@
"hosted-git-info": "^2.1.5",
"image-size": "^0.5.0",
"isbinaryfile": "^3.0.1",
- "lodash.template": "^4.3.0",
+ "lodash.template": "^4.4.0",
"mime": "^1.3.4",
"minimatch": "^3.0.3",
"normalize-package-data": "^2.3.5",
diff --git a/src/metadata.ts b/src/metadata.ts
index 29c1b1005a9..c5b933b535b 100755
--- a/src/metadata.ts
+++ b/src/metadata.ts
@@ -488,6 +488,11 @@ export interface FileAssociation {
The name. e.g. `PNG`
*/
readonly name: string
+
+ /*
+ *windows-only.* The description.
+ */
+ readonly description?: string
}
/*
diff --git a/src/platformPackager.ts b/src/platformPackager.ts
index ccf6e70fd0c..819a7af4fbb 100644
--- a/src/platformPackager.ts
+++ b/src/platformPackager.ts
@@ -122,9 +122,7 @@ export abstract class PlatformPackager
return options == null ? Object.create(null) : options
}
- createTargets(targets: Array, mapper: (name: string, factory: (outDir: string) => Target) => void, cleanupTasks: Array<() => Promise>): void {
- throw new Error("not implemented")
- }
+ abstract createTargets(targets: Array, mapper: (name: string, factory: (outDir: string) => Target) => void, cleanupTasks: Array<() => Promise>): void
protected getCscPassword(): string {
const password = this.options.cscKeyPassword || process.env.CSC_KEY_PASSWORD
diff --git a/src/targets/LinuxTargetHelper.ts b/src/targets/LinuxTargetHelper.ts
index 63a0935ef7a..ff7f3ab2944 100644
--- a/src/targets/LinuxTargetHelper.ts
+++ b/src/targets/LinuxTargetHelper.ts
@@ -19,11 +19,8 @@ export class LinuxTargetHelper {
constructor(private packager: PlatformPackager, cleanupTasks: Array<() => Promise>) {
const tempDir = path.join(tmpdir(), getTempName("electron-builder-linux"))
- this.tempDirPromise = emptyDir(tempDir)
- .then(() => {
- cleanupTasks.push(() => remove(tempDir))
- return tempDir
- })
+ this.tempDirPromise = emptyDir(tempDir).thenReturn(tempDir)
+ cleanupTasks.push(() => remove(tempDir))
this.icons = this.computeDesktopIcons()
}
diff --git a/src/targets/nsis.ts b/src/targets/nsis.ts
index 7092c1876af..33b067d5b11 100644
--- a/src/targets/nsis.ts
+++ b/src/targets/nsis.ts
@@ -1,15 +1,16 @@
import { WinPackager } from "../winPackager"
import { Arch, NsisOptions } from "../metadata"
-import { debug, doSpawn, handleProcess, use } from "../util/util"
+import { exec, debug, doSpawn, handleProcess, use, getTempName } from "../util/util"
import * as path from "path"
import { Promise as BluebirdPromise } from "bluebird"
import { getBinFromBintray } from "../util/binDownload"
import { v5 as uuid5 } from "uuid-1345"
import { Target } from "../platformPackager"
import { archiveApp } from "./archive"
-import { subTask, task } from "../util/log"
-import { unlink, readFile } from "fs-extra-p"
+import { subTask, task, log } from "../util/log"
+import { unlink, readFile, remove } from "fs-extra-p"
import semver = require("semver")
+import { tmpdir } from "os"
//noinspection JSUnusedLocalSymbols
const __awaiter = require("../util/awaiter")
@@ -28,7 +29,9 @@ export default class NsisTarget extends Target {
private archs: Map> = new Map()
- constructor(private packager: WinPackager, private outDir: string) {
+ private readonly nsisTemplatesDir = path.join(__dirname, "..", "..", "templates", "nsis")
+
+ constructor(private packager: WinPackager, private outDir: string, private cleanupTasks: Array<() => Promise>) {
super("nsis")
this.options = packager.info.devMetadata.build.nsis || Object.create(null)
@@ -52,8 +55,8 @@ export default class NsisTarget extends Target {
const iconPath = await packager.getIconPath()
const appInfo = packager.appInfo
const version = appInfo.version
- const installerPath = path.join(this.outDir, `${appInfo.productFilename} Setup ${version}.exe`)
+ const installerPath = path.join(this.outDir, `${appInfo.productFilename} Setup ${version}.exe`)
const guid = this.options.guid || await BluebirdPromise.promisify(uuid5)({namespace: ELECTRON_BUILDER_NS_UUID, name: appInfo.id})
const defines: any = {
APP_ID: appInfo.id,
@@ -122,9 +125,7 @@ export default class NsisTarget extends Target {
const commands: any = {
OutFile: `"${installerPath}"`,
- // LoadLanguageFile: '"${NSISDIR}/Contrib/Language files/English.nlf"',
VIProductVersion: `${parsedVersion.major}.${parsedVersion.minor}.${parsedVersion.patch}.${appInfo.buildNumber || "0"}`,
- // VIFileVersion: packager.appInfo.buildVersion,
VIAddVersionKey: versionKey,
}
@@ -151,7 +152,29 @@ export default class NsisTarget extends Target {
return
}
- await subTask(`Executing makensis`, this.executeMakensis(defines, commands))
+ const customScriptPath = await this.getResource(this.options.script, "installer.nsi")
+ const script = await readFile(customScriptPath || path.join(this.nsisTemplatesDir, "installer.nsi"), "utf8")
+
+ if (customScriptPath == null) {
+ const uninstallerPath = path.join(tmpdir(), `${getTempName("electron-builder")}.exe`)
+ this.cleanupTasks.push(() => remove(uninstallerPath))
+
+ defines.BUILD_UNINSTALLER = null
+ defines.UNINSTALLER_OUT_FILE = path.win32.join("Z:", uninstallerPath)
+ await subTask(`Executing makensis — uninstaller`, this.executeMakensis(defines, commands, false, script))
+ const isWin = process.platform === "win32"
+ await exec(isWin ? installerPath : "wine", isWin ? [] : [installerPath])
+ await packager.sign(uninstallerPath)
+
+ delete defines.BUILD_UNINSTALLER
+ // platform-specific path, not wine
+ defines.UNINSTALLER_OUT_FILE = uninstallerPath
+ }
+ else {
+ log("Custom NSIS script is used - uninstaller is not signed by electron-builder")
+ }
+
+ await subTask(`Executing makensis — installer`, this.executeMakensis(defines, commands, true, script))
await packager.sign(installerPath)
this.packager.dispatchArtifactCreated(installerPath, `${appInfo.name}-Setup-${version}.exe`)
@@ -172,7 +195,7 @@ export default class NsisTarget extends Target {
return null
}
- private async executeMakensis(defines: any, commands: any) {
+ private async executeMakensis(defines: any, commands: any, isInstaller: boolean, originalScript: string) {
const args: Array = ["-WX"]
for (let name of Object.keys(defines)) {
const value = defines[name]
@@ -196,18 +219,18 @@ export default class NsisTarget extends Target {
}
}
- const nsisTemplatesDir = path.join(__dirname, "..", "..", "templates", "nsis")
args.push("-")
const binDir = process.platform === "darwin" ? "mac" : (process.platform === "win32" ? "Bin" : "linux")
const nsisPath = await nsisPathPromise
const packager = this.packager
+ // CFBundleTypeName
+ // https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685
+ // CFBundleTypeExtensions
const fileAssociations = asArray(packager.devMetadata.build.fileAssociations).concat(asArray(packager.platformSpecificBuildOptions.fileAssociations))
- let registerFileAssociationsScript = ""
- let unregisterFileAssociationsScript = ""
- let script = await readFile((await this.getResource(this.options.script, "installer.nsi")) || path.join(nsisTemplatesDir, "installer.nsi"), "utf8")
+ let script = originalScript
const customInclude = await this.getResource(this.options.include, "installer.nsh")
if (customInclude != null) {
script = `!include "${customInclude}"\n!addincludedir "${this.packager.buildResourcesDir}"\n${script}`
@@ -215,18 +238,25 @@ export default class NsisTarget extends Target {
if (fileAssociations.length !== 0) {
script = "!include FileAssociation.nsh\n" + script
- for (let item of fileAssociations) {
- registerFileAssociationsScript += '${RegisterExtension} "$INSTDIR\\${APP_EXECUTABLE_FILENAME}" ' + `"${normalizeExt(item.ext)}" "${item.name}"\n`
+ if (isInstaller) {
+ let registerFileAssociationsScript = ""
+ for (let item of fileAssociations) {
+ const icon = '"$INSTDIR\\${APP_EXECUTABLE_FILENAME},0"'
+ const commandText = `"Open with ${this.packager.appInfo.productName}"`
+ const command = '"$INSTDIR\\${APP_EXECUTABLE_FILENAME} $\\"%1$\\""'
+ registerFileAssociationsScript += ` !insertmacro APP_ASSOCIATE "${normalizeExt(item.ext)}" "${item.name}" "${item.description || ""}" ${icon} ${commandText} ${command}\n`
+ }
+ script = `!macro registerFileAssociations\n${registerFileAssociationsScript}!macroend\n${script}`
}
-
- for (let item of fileAssociations) {
- unregisterFileAssociationsScript += "${UnRegisterExtension} " + `"${normalizeExt(item.ext)}" "${item.name}"\n`
+ else {
+ let unregisterFileAssociationsScript = ""
+ for (let item of fileAssociations) {
+ unregisterFileAssociationsScript += ` !insertmacro APP_UNASSOCIATE "${normalizeExt(item.ext)}" "${item.name}"\n`
+ }
+ script = `!macro unregisterFileAssociations\n${unregisterFileAssociationsScript}!macroend\n${script}`
}
}
- script = script.replace("!insertmacro registerFileAssociations", registerFileAssociationsScript)
- script = script.replace("!insertmacro unregisterFileAssociations", unregisterFileAssociationsScript)
-
if (debug.enabled) {
process.stdout.write("\n\nNSIS script:\n\n" + script + "\n\n---\nEnd of NSIS script.\n\n")
}
@@ -236,7 +266,7 @@ export default class NsisTarget extends Target {
const childProcess = doSpawn(command, args, {
// 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: Object.assign({}, process.env, {NSISDIR: nsisPath}),
- cwd: nsisTemplatesDir,
+ cwd: this.nsisTemplatesDir,
}, true)
handleProcess("close", childProcess, command, resolve, reject)
@@ -245,9 +275,9 @@ export default class NsisTarget extends Target {
}
}
-// nsis — add leading dot, mac — remove leading dot
+// remove leading dot
function normalizeExt(ext: string) {
- return ext.startsWith(".") ? ext : `.${ext}`
+ return ext.startsWith(".") ? ext.substring(1) : ext
}
function asArray(v: n | T | Array): Array {
diff --git a/src/targets/squirrelWindows.ts b/src/targets/squirrelWindows.ts
index deb42b094dc..3e2f502579f 100644
--- a/src/targets/squirrelWindows.ts
+++ b/src/targets/squirrelWindows.ts
@@ -18,7 +18,7 @@ const SW_VERSION = "1.4.4"
const SW_SHA2 = "98e1d81c80d7afc1bcfb37f3b224dc4f761088506b9c28ccd72d1cf8752853ba"
export default class SquirrelWindowsTarget extends Target {
- constructor(private packager: WinPackager) {
+ constructor(private packager: WinPackager, private cleanupTasks: Array<() => Promise>) {
super("squirrel")
}
@@ -38,17 +38,8 @@ export default class SquirrelWindowsTarget extends Target {
const stageDir = path.join(tmpdir(), getTempName("squirrel-windows-builder"))
await emptyDir(stageDir)
- try {
- await buildInstaller(distOptions, installerOutDir, stageDir, setupFileName, this.packager, appOutDir)
- }
- finally {
- try {
- await remove(stageDir)
- }
- catch (e) {
- // ignore
- }
- }
+ this.cleanupTasks.push(() => remove(stageDir))
+ await buildInstaller(distOptions, installerOutDir, stageDir, setupFileName, this.packager, appOutDir)
this.packager.dispatchArtifactCreated(path.join(installerOutDir, setupFileName), `${appInfo.name}-Setup-${version}${archSuffix}.exe`)
diff --git a/src/winPackager.ts b/src/winPackager.ts
index 4e8c1a08d40..5456324cd45 100644
--- a/src/winPackager.ts
+++ b/src/winPackager.ts
@@ -75,13 +75,13 @@ export class WinPackager extends PlatformPackager {
if (name === DEFAULT_TARGET || name === "squirrel") {
mapper("squirrel", () => {
const targetClass: typeof SquirrelWindowsTarget = require("./targets/squirrelWindows").default
- return new targetClass(this)
+ return new targetClass(this, cleanupTasks)
})
}
else if (name === "nsis") {
mapper(name, outDir => {
const targetClass: typeof NsisTarget = require("./targets/nsis").default
- return new targetClass(this, outDir)
+ return new targetClass(this, outDir, cleanupTasks)
})
}
else {
diff --git a/templates/nsis/FileAssociation.nsh b/templates/nsis/FileAssociation.nsh
index fa4885e3816..711be1a750d 100644
--- a/templates/nsis/FileAssociation.nsh
+++ b/templates/nsis/FileAssociation.nsh
@@ -1,176 +1,117 @@
-/*
-_____________________________________________________________________________
- File Association
-_____________________________________________________________________________
- Based on code taken from http://nsis.sourceforge.net/File_Association
- Usage in script:
- 1. !include "FileAssociation.nsh"
- 2. [Section|Function]
- ${FileAssociationFunction} "Param1" "Param2" "..." $var
- [SectionEnd|FunctionEnd]
- FileAssociationFunction=[RegisterExtension|UnRegisterExtension]
-_____________________________________________________________________________
- ${RegisterExtension} "[executable]" "[extension]" "[description]"
-"[executable]" ; executable which opens the file format
- ;
-"[extension]" ; extension, which represents the file format to open
- ;
-"[description]" ; description for the extension. This will be display in Windows Explorer.
- ;
- ${UnRegisterExtension} "[extension]" "[description]"
-"[extension]" ; extension, which represents the file format to open
- ;
-"[description]" ; description for the extension. This will be display in Windows Explorer.
- ;
-_____________________________________________________________________________
- Macros
-_____________________________________________________________________________
- Change log window verbosity (default: 3=no script)
- Example:
- !include "FileAssociation.nsh"
- !insertmacro RegisterExtension
- ${FileAssociation_VERBOSE} 4 # all verbosity
- !insertmacro UnRegisterExtension
- ${FileAssociation_VERBOSE} 3 # no script
-*/
-
-
-!ifndef FileAssociation_INCLUDED
-!define FileAssociation_INCLUDED
-
-!include Util.nsh
-
-!verbose push
-!verbose 3
-!ifndef _FileAssociation_VERBOSE
- !define _FileAssociation_VERBOSE 3
-!endif
-!verbose ${_FileAssociation_VERBOSE}
-!define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE`
-!verbose pop
-
-!macro FileAssociation_VERBOSE _VERBOSE
- !verbose push
- !verbose 3
- !undef _FileAssociation_VERBOSE
- !define _FileAssociation_VERBOSE ${_VERBOSE}
- !verbose pop
-!macroend
-
-
-
-!macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION
- !verbose push
- !verbose ${_FileAssociation_VERBOSE}
- Push `${_DESCRIPTION}`
- Push `${_EXTENSION}`
- Push `${_EXECUTABLE}`
- ${CallArtificialFunction} RegisterExtension_
- !verbose pop
-!macroend
-
-!macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION
- !verbose push
- !verbose ${_FileAssociation_VERBOSE}
- Push `${_EXTENSION}`
- Push `${_DESCRIPTION}`
- ${CallArtificialFunction} UnRegisterExtension_
- !verbose pop
+; fileassoc.nsh
+; File association helper macros
+; Written by Saivert
+;
+; Features automatic backup system and UPDATEFILEASSOC macro for
+; shell change notification.
+;
+; |> How to use <|
+; To associate a file with an application so you can double-click it in explorer, use
+; the APP_ASSOCIATE macro like this:
+;
+; Example:
+; !insertmacro APP_ASSOCIATE "txt" "myapp.textfile" "Description of txt files" \
+; "$INSTDIR\myapp.exe,0" "Open with myapp" "$INSTDIR\myapp.exe $\"%1$\""
+;
+; Never insert the APP_ASSOCIATE macro multiple times, it is only ment
+; to associate an application with a single file and using the
+; the "open" verb as default. To add more verbs (actions) to a file
+; use the APP_ASSOCIATE_ADDVERB macro.
+;
+; Example:
+; !insertmacro APP_ASSOCIATE_ADDVERB "myapp.textfile" "edit" "Edit with myapp" \
+; "$INSTDIR\myapp.exe /edit $\"%1$\""
+;
+; To have access to more options when registering the file association use the
+; APP_ASSOCIATE_EX macro. Here you can specify the verb and what verb is to be the
+; standard action (default verb).
+;
+; And finally: To remove the association from the registry use the APP_UNASSOCIATE
+; macro. Here is another example just to wrap it up:
+; !insertmacro APP_UNASSOCIATE "txt" "myapp.textfile"
+;
+; |> Note <|
+; When defining your file class string always use the short form of your application title
+; then a period (dot) and the type of file. This keeps the file class sort of unique.
+; Examples:
+; Winamp.Playlist
+; NSIS.Script
+; Photoshop.JPEGFile
+;
+; |> Tech info <|
+; The registry key layout for a file association is:
+; HKEY_CLASSES_ROOT
+; = <"description">
+; shell
+; = <"menu-item text">
+; command = <"command string">
+;
+
+!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND
+ ; Backup the previously associated file class
+ ReadRegStr $R0 HKCR ".${EXT}" ""
+ WriteRegStr HKCR ".${EXT}" "${FILECLASS}_backup" "$R0"
+
+ WriteRegStr HKCR ".${EXT}" "" "${FILECLASS}"
+
+ WriteRegStr HKCR "${FILECLASS}" "" `${DESCRIPTION}`
+ WriteRegStr HKCR "${FILECLASS}\DefaultIcon" "" `${ICON}`
+ WriteRegStr HKCR "${FILECLASS}\shell" "" "open"
+ WriteRegStr HKCR "${FILECLASS}\shell\open" "" `${COMMANDTEXT}`
+ WriteRegStr HKCR "${FILECLASS}\shell\open\command" "" `${COMMAND}`
!macroend
-
-
-
-!define RegisterExtension `!insertmacro RegisterExtensionCall`
-!define un.RegisterExtension `!insertmacro RegisterExtensionCall`
-
-!macro RegisterExtension
+
+!macro APP_ASSOCIATE_EX EXT FILECLASS DESCRIPTION ICON VERB DEFAULTVERB SHELLNEW COMMANDTEXT COMMAND
+ ; Backup the previously associated file class
+ ReadRegStr $R0 HKCR ".${EXT}" ""
+ WriteRegStr HKCR ".${EXT}" "${FILECLASS}_backup" "$R0"
+
+ WriteRegStr HKCR ".${EXT}" "" "${FILECLASS}"
+ StrCmp "${SHELLNEW}" "0" +2
+ WriteRegStr HKCR ".${EXT}\ShellNew" "NullFile" ""
+
+ WriteRegStr HKCR "${FILECLASS}" "" `${DESCRIPTION}`
+ WriteRegStr HKCR "${FILECLASS}\DefaultIcon" "" `${ICON}`
+ WriteRegStr HKCR "${FILECLASS}\shell" "" `${DEFAULTVERB}`
+ WriteRegStr HKCR "${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}`
+ WriteRegStr HKCR "${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}`
!macroend
-
-!macro un.RegisterExtension
+
+!macro APP_ASSOCIATE_ADDVERB FILECLASS VERB COMMANDTEXT COMMAND
+ WriteRegStr HKCR "${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}`
+ WriteRegStr HKCR "${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}`
!macroend
-
-!macro RegisterExtension_
- !verbose push
- !verbose ${_FileAssociation_VERBOSE}
-
- Exch $R2 ;exe
- Exch
- Exch $R1 ;ext
- Exch
- Exch 2
- Exch $R0 ;desc
- Exch 2
- Push $0
- Push $1
-
- ReadRegStr $1 HKCR $R1 "" ; read current file association
- StrCmp "$1" "" NoBackup ; is it empty
- StrCmp "$1" "$R0" NoBackup ; is it our own
- WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value
-NoBackup:
- WriteRegStr HKCR $R1 "" "$R0" ; set our file association
-
- ReadRegStr $0 HKCR $R0 ""
- StrCmp $0 "" 0 Skip
- WriteRegStr HKCR "$R0" "" "$R0"
- WriteRegStr HKCR "$R0\shell" "" "open"
- WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0"
-Skip:
- WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"'
- WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0"
- WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"'
-
- Pop $1
- Pop $0
- Pop $R2
- Pop $R1
- Pop $R0
-
- !verbose pop
+
+!macro APP_ASSOCIATE_REMOVEVERB FILECLASS VERB
+ DeleteRegKey HKCR `${FILECLASS}\shell\${VERB}`
!macroend
-
-
-
-!define UnRegisterExtension `!insertmacro UnRegisterExtensionCall`
-!define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall`
-
-!macro UnRegisterExtension
+
+
+!macro APP_UNASSOCIATE EXT FILECLASS
+ ; Backup the previously associated file class
+ ReadRegStr $R0 HKCR ".${EXT}" `${FILECLASS}_backup`
+ WriteRegStr HKCR ".${EXT}" "" "$R0"
+
+ DeleteRegKey HKCR `${FILECLASS}`
!macroend
-
-!macro un.UnRegisterExtension
+
+!macro APP_ASSOCIATE_GETFILECLASS OUTPUT EXT
+ ReadRegStr ${OUTPUT} HKCR ".${EXT}" ""
!macroend
-
-!macro UnRegisterExtension_
- !verbose push
- !verbose ${_FileAssociation_VERBOSE}
-
- Exch $R1 ;desc
- Exch
- Exch $R0 ;ext
- Exch
- Push $0
- Push $1
-
- ReadRegStr $1 HKCR $R0 ""
- StrCmp $1 $R1 0 NoOwn ; only do this if we own it
- ReadRegStr $1 HKCR $R0 "backup_val"
- StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key
- DeleteRegKey HKCR $R0
- Goto NoOwn
-
-Restore:
- WriteRegStr HKCR $R0 "" $1
- DeleteRegValue HKCR $R0 "backup_val"
- DeleteRegKey HKCR $R1 ;Delete key with association name settings
-
-NoOwn:
-
- Pop $1
- Pop $0
- Pop $R1
- Pop $R0
-
- !verbose pop
-!macroend
-
-!endif # !FileAssociation_INCLUDED
\ No newline at end of file
+
+
+; !defines for use with SHChangeNotify
+!ifdef SHCNE_ASSOCCHANGED
+!undef SHCNE_ASSOCCHANGED
+!endif
+!define SHCNE_ASSOCCHANGED 0x08000000
+!ifdef SHCNF_FLUSH
+!undef SHCNF_FLUSH
+!endif
+!define SHCNF_FLUSH 0x1000
+
+!macro UPDATEFILEASSOC
+; Using the system.dll plugin to call the SHChangeNotify Win32 API function so we
+; can update the shell.
+ System::Call "shell32::SHChangeNotify(i,i,i,i) (${SHCNE_ASSOCCHANGED}, ${SHCNF_FLUSH}, 0, 0)"
+!macroend
\ No newline at end of file
diff --git a/templates/nsis/boringInstaller.nsh b/templates/nsis/boringInstaller.nsh
index a221621ff42..8ba73347839 100644
--- a/templates/nsis/boringInstaller.nsh
+++ b/templates/nsis/boringInstaller.nsh
@@ -1,87 +1,78 @@
-# http://nsis.sourceforge.net/Run_an_application_shortcut_after_an_install
!include multiUserUi.nsh
-Function StartApp
- !insertmacro UAC_AsUser_ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk" "" "" ""
-FunctionEnd
+!ifndef BUILD_UNINSTALLER
+ Function StartApp
+ !insertmacro UAC_AsUser_ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk" "" "" ""
+ FunctionEnd
-!define MUI_FINISHPAGE_RUN
-!define MUI_FINISHPAGE_RUN_FUNCTION "StartApp"
+ Function GuiInit
+ !insertmacro UAC_PageElevation_OnGuiInit
+ FunctionEnd
-!define MUI_CUSTOMFUNCTION_GUIINIT GuiInit
+ !define MUI_FINISHPAGE_RUN
+ !define MUI_FINISHPAGE_RUN_FUNCTION "StartApp"
-!insertmacro MULTIUSER_PAGE_INSTALLMODE
-!insertmacro MUI_PAGE_INSTFILES
-!insertmacro MUI_PAGE_FINISH
+ !define MUI_CUSTOMFUNCTION_GUIINIT GuiInit
-# uninstall pages
-!insertmacro MUI_UNPAGE_INSTFILES
+ !insertmacro PAGE_INSTALL_MODE
+ !insertmacro MUI_PAGE_INSTFILES
+ !insertmacro MUI_PAGE_FINISH
+!else
+ !insertmacro PAGE_INSTALL_MODE
+ !insertmacro MUI_UNPAGE_INSTFILES
+!endif
-!insertmacro MUI_LANGUAGE "English"
-!insertmacro MUI_LANGUAGE "German"
-!insertmacro MUI_LANGUAGE "French"
-!insertmacro MUI_LANGUAGE "Spanish"
-!insertmacro MUI_LANGUAGE "SpanishInternational"
-!insertmacro MUI_LANGUAGE "SimpChinese"
-!insertmacro MUI_LANGUAGE "TradChinese"
-!insertmacro MUI_LANGUAGE "Japanese"
-#!insertmacro MUI_LANGUAGE "Korean"
-!insertmacro MUI_LANGUAGE "Italian"
-!insertmacro MUI_LANGUAGE "Dutch"
-#!insertmacro MUI_LANGUAGE "Danish"
-#!insertmacro MUI_LANGUAGE "Swedish"
-#!insertmacro MUI_LANGUAGE "Norwegian"
-#!insertmacro MUI_LANGUAGE "NorwegianNynorsk"
-#!insertmacro MUI_LANGUAGE "Finnish"
-#!insertmacro MUI_LANGUAGE "Greek"
-!insertmacro MUI_LANGUAGE "Russian"
-#!insertmacro MUI_LANGUAGE "Portuguese"
-#!insertmacro MUI_LANGUAGE "PortugueseBR"
-#!insertmacro MUI_LANGUAGE "Polish"
-#!insertmacro MUI_LANGUAGE "Ukrainian"
-!insertmacro MUI_LANGUAGE "Czech"
-#!insertmacro MUI_LANGUAGE "Slovak"
-#!insertmacro MUI_LANGUAGE "Croatian"
-#!insertmacro MUI_LANGUAGE "Bulgarian"
-#!insertmacro MUI_LANGUAGE "Hungarian"
-#!insertmacro MUI_LANGUAGE "Thai"
-#!insertmacro MUI_LANGUAGE "Romanian"
-#!insertmacro MUI_LANGUAGE "Latvian"
-#!insertmacro MUI_LANGUAGE "Macedonian"
-#!insertmacro MUI_LANGUAGE "Estonian"
-#!insertmacro MUI_LANGUAGE "Turkish"
-#!insertmacro MUI_LANGUAGE "Lithuanian"
-#!insertmacro MUI_LANGUAGE "Slovenian"
-#!insertmacro MUI_LANGUAGE "Serbian"
-#!insertmacro MUI_LANGUAGE "SerbianLatin"
-#!insertmacro MUI_LANGUAGE "Arabic"
-#!insertmacro MUI_LANGUAGE "Farsi"
-#!insertmacro MUI_LANGUAGE "Hebrew"
-#!insertmacro MUI_LANGUAGE "Indonesian"
-#!insertmacro MUI_LANGUAGE "Mongolian"
-#!insertmacro MUI_LANGUAGE "Luxembourgish"
-#!insertmacro MUI_LANGUAGE "Albanian"
-#!insertmacro MUI_LANGUAGE "Breton"
-#!insertmacro MUI_LANGUAGE "Belarusian"
-#!insertmacro MUI_LANGUAGE "Icelandic"
-#!insertmacro MUI_LANGUAGE "Malay"
-#!insertmacro MUI_LANGUAGE "Bosnian"
-#!insertmacro MUI_LANGUAGE "Kurdish"
-#!insertmacro MUI_LANGUAGE "Irish"
-#!insertmacro MUI_LANGUAGE "Uzbek"
-#!insertmacro MUI_LANGUAGE "Galician"
-#!insertmacro MUI_LANGUAGE "Afrikaans"
-#!insertmacro MUI_LANGUAGE "Catalan"
-#!insertmacro MUI_LANGUAGE "Esperanto"
-#!insertmacro MUI_LANGUAGE "Asturian"
-#!insertmacro MUI_LANGUAGE "Basque"
-#!insertmacro MUI_LANGUAGE "Pashto"
-#!insertmacro MUI_LANGUAGE "ScotsGaelic"
-#!insertmacro MUI_LANGUAGE "Georgian"
-#!insertmacro MUI_LANGUAGE "Vietnamese"
-#!insertmacro MUI_LANGUAGE "Welsh"
-#!insertmacro MUI_LANGUAGE "Armenian"
+!macro initMultiUser UNINSTALLER_FUNCPREFIX
+ !insertmacro UAC_PageElevation_OnInit
-Function GuiInit
- !insertmacro UAC_PageElevation_OnGuiInit
-FunctionEnd
\ No newline at end of file
+ ${If} ${UAC_IsInnerInstance}
+ ${AndIfNot} ${UAC_IsAdmin}
+ # special return value for outer instance so it knows we did not have admin rights
+ SetErrorLevel 0x666666
+ Quit
+ ${EndIf}
+
+ !ifndef MULTIUSER_INIT_TEXT_ADMINREQUIRED
+ !define MULTIUSER_INIT_TEXT_ADMINREQUIRED "$(^Caption) requires administrator privileges."
+ !endif
+
+ !ifndef MULTIUSER_INIT_TEXT_POWERREQUIRED
+ !define MULTIUSER_INIT_TEXT_POWERREQUIRED "$(^Caption) requires at least Power User privileges."
+ !endif
+
+ !ifndef MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE
+ !define MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE "Your user account does not have sufficient privileges to install $(^Name) for all users of this computer."
+ !endif
+
+ # checks registry for previous installation path (both for upgrading, reinstall, or uninstall)
+ StrCpy $hasPerMachineInstallation "0"
+ StrCpy $hasPerUserInstallation "0"
+
+ # set installation mode to setting from a previous installation
+ ReadRegStr $perMachineInstallationFolder HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation
+ ${if} $perMachineInstallationFolder != ""
+ StrCpy $hasPerMachineInstallation "1"
+ ${endif}
+
+ ReadRegStr $perUserInstallationFolder HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation
+ ${if} $perUserInstallationFolder != ""
+ StrCpy $hasPerUserInstallation "1"
+ ${endif}
+
+ ${if} $hasPerUserInstallation == "1"
+ ${andif} $hasPerMachineInstallation == "0"
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.CurrentUser
+ ${elseif} $hasPerUserInstallation == "0"
+ ${andif} $hasPerMachineInstallation == "1"
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.AllUsers
+ ${else}
+ # if there is no installation, or there is both per-user and per-machine
+ !ifdef INSTALL_MODE_PER_ALL_USERS
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.AllUsers
+ !else
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.CurrentUser
+ !endif
+ ${endif}
+!macroend
+
+!include "langs.nsh"
\ No newline at end of file
diff --git a/templates/nsis/common.nsh b/templates/nsis/common.nsh
index d28e84ee16c..b81f2ca248f 100644
--- a/templates/nsis/common.nsh
+++ b/templates/nsis/common.nsh
@@ -3,13 +3,13 @@
BrandingText "${PRODUCT_NAME} ${VERSION}"
ShowInstDetails nevershow
-ShowUninstDetails nevershow
+!ifdef BUILD_UNINSTALLER
+ ShowUninstDetails nevershow
+!endif
FileBufSize 64
Name "${PRODUCT_NAME}"
Unicode true
-!define MULTIUSER_INSTALLMODE_INSTDIR "${APP_GUID}"
-
!define APP_EXECUTABLE_FILENAME "${PRODUCT_FILENAME}.exe"
!define UNINSTALL_FILENAME "Uninstall ${PRODUCT_FILENAME}.exe"
diff --git a/templates/nsis/installer.nsi b/templates/nsis/installer.nsi
index 8ba9571aa28..d2ae571ef7e 100644
--- a/templates/nsis/installer.nsi
+++ b/templates/nsis/installer.nsi
@@ -16,151 +16,113 @@
Var startMenuLink
Var desktopLink
-Function .onInit
- !insertmacro check64BitAndSetRegView
- !insertmacro initMultiUser "" ""
+!ifdef BUILD_UNINSTALLER
+ SilentInstall silent
+!endif
- !ifdef ONE_CLICK
- !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTACE
+Function .onInit
+ !ifdef BUILD_UNINSTALLER
+ WriteUninstaller "${UNINSTALLER_OUT_FILE}"
+ # avoid exit code 2
+ SetErrorLevel 0
+ Quit
!else
- ${IfNot} ${UAC_IsInnerInstance}
- !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTACE
- ${EndIf}
- !endif
-
- InitPluginsDir
-
- SetCompress off
- !ifdef APP_32
- File /oname=$PLUGINSDIR\app-32.7z "${APP_32}"
- !endif
- !ifdef APP_64
- File /oname=$PLUGINSDIR\app-64.7z "${APP_64}"
- !endif
- SetCompress "${COMPRESS}"
-
- !ifdef HEADER_ICO
- File /oname=$PLUGINSDIR\installerHeaderico.ico "${HEADER_ICO}"
- !endif
-
- !ifmacrodef customInit
- !insertmacro customInit
- !endif
-FunctionEnd
-
-Section "install"
- ${IfNot} ${Silent}
- SetDetailsPrint none
+ !insertmacro check64BitAndSetRegView
+ !insertmacro initMultiUser ""
!ifdef ONE_CLICK
- !ifdef HEADER_ICO
- SpiderBanner::Show /MODERN /ICON "$PLUGINSDIR\installerHeaderico.ico"
- !else
- SpiderBanner::Show /MODERN
- !endif
+ !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTACE
+ !else
+ ${IfNot} ${UAC_IsInnerInstance}
+ !insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTACE
+ ${EndIf}
!endif
- ${EndIf}
-
- !insertmacro CHECK_APP_RUNNING "install"
-
- RMDir /r $INSTDIR
- SetOutPath $INSTDIR
-
- !ifdef APP_64
- ${If} ${RunningX64}
- Nsis7z::Extract "$PLUGINSDIR\app-64.7z"
- ${Else}
- Nsis7z::Extract "$PLUGINSDIR\app-32.7z"
- ${EndIf}
- !else
- Nsis7z::Extract "$PLUGINSDIR\app-32.7z"
- !endif
-
- WriteUninstaller "${UNINSTALL_FILENAME}"
- !insertmacro MULTIUSER_RegistryAddInstallInfo
+ InitPluginsDir
- StrCpy $startMenuLink "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
- StrCpy $desktopLink "$DESKTOP\${PRODUCT_FILENAME}.lnk"
-
- # create shortcuts in the start menu and on the desktop
- # shortcut for uninstall is bad cause user can choose this by mistake during search, so, we don't add it
- CreateShortCut "$startMenuLink" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" 0 "" "" "${APP_DESCRIPTION}"
- CreateShortCut "$desktopLink" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" 0 "" "" "${APP_DESCRIPTION}"
-
- WinShell::SetLnkAUMI "$startMenuLink" "${APP_ID}"
- WinShell::SetLnkAUMI "$desktopLink" "${APP_ID}"
+ SetCompress off
+ !ifdef APP_32
+ File /oname=$PLUGINSDIR\app-32.7z "${APP_32}"
+ !endif
+ !ifdef APP_64
+ File /oname=$PLUGINSDIR\app-64.7z "${APP_64}"
+ !endif
+ SetCompress "${COMPRESS}"
- !insertmacro registerFileAssociations
+ !ifdef HEADER_ICO
+ File /oname=$PLUGINSDIR\installerHeaderico.ico "${HEADER_ICO}"
+ !endif
- !ifmacrodef customInstall
- !insertmacro customInstall
+ !ifmacrodef customInit
+ !insertmacro customInit
+ !endif
!endif
+FunctionEnd
- ${IfNot} ${Silent}
- !ifdef ONE_CLICK
- # otherwise app window will be in backround
- HideWindow
- !ifdef RUN_AFTER_FINISH
- Call StartApp
+!ifndef BUILD_UNINSTALLER
+ Section "install"
+ ${IfNot} ${Silent}
+ SetDetailsPrint none
+
+ !ifdef ONE_CLICK
+ !ifdef HEADER_ICO
+ SpiderBanner::Show /MODERN /ICON "$PLUGINSDIR\installerHeaderico.ico"
+ !else
+ SpiderBanner::Show /MODERN
+ !endif
!endif
- !endif
- ${EndIf}
-SectionEnd
+ ${EndIf}
-Function un.onInit
- !insertmacro check64BitAndSetRegView
+ !insertmacro CHECK_APP_RUNNING "install"
- ${IfNot} ${Silent}
- MessageBox MB_OKCANCEL "Are you sure you want to uninstall ${PRODUCT_NAME}?" IDOK +2
- Quit
+ RMDir /r $INSTDIR
+ SetOutPath $INSTDIR
- !ifdef ONE_CLICK
- # one-click installer executes uninstall section in the silent mode, but we must show message dialog if silent mode was not explicitly set by user (using /S flag)
- !insertmacro CHECK_APP_RUNNING "uninstall"
- SetSilent silent
+ !ifdef APP_64
+ ${If} ${RunningX64}
+ Nsis7z::Extract "$PLUGINSDIR\app-64.7z"
+ ${Else}
+ Nsis7z::Extract "$PLUGINSDIR\app-32.7z"
+ ${EndIf}
+ !else
+ Nsis7z::Extract "$PLUGINSDIR\app-32.7z"
!endif
- ${EndIf}
- !insertmacro initMultiUser Un un.
+ File "/oname=${UNINSTALL_FILENAME}" "${UNINSTALLER_OUT_FILE}"
- !ifmacrodef customUnInit
- !insertmacro customUnInit
- !endif
-FunctionEnd
-
-Section "un.install"
- SetAutoClose true
-
- !ifndef ONE_CLICK
- # for boring installer we check it here to show progress
- !insertmacro CHECK_APP_RUNNING "uninstall"
- !endif
-
- StrCpy $startMenuLink "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
- StrCpy $desktopLink "$DESKTOP\${PRODUCT_FILENAME}.lnk"
-
- WinShell::UninstAppUserModelId "${APP_ID}"
- WinShell::UninstShortcut "$startMenuLink"
- WinShell::UninstShortcut "$desktopLink"
+ !insertmacro registryAddInstallInfo
- Delete "$startMenuLink"
- Delete "$desktopLink"
+ StrCpy $startMenuLink "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
+ StrCpy $desktopLink "$DESKTOP\${PRODUCT_FILENAME}.lnk"
- !insertmacro unregisterFileAssociations
+ # create shortcuts in the start menu and on the desktop
+ # shortcut for uninstall is bad cause user can choose this by mistake during search, so, we don't add it
+ CreateShortCut "$startMenuLink" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" 0 "" "" "${APP_DESCRIPTION}"
+ CreateShortCut "$desktopLink" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" 0 "" "" "${APP_DESCRIPTION}"
- # delete the installed files
- RMDir /r $INSTDIR
+ WinShell::SetLnkAUMI "$startMenuLink" "${APP_ID}"
+ WinShell::SetLnkAUMI "$desktopLink" "${APP_ID}"
- ${GetParameters} $R0
- ${GetOptions} $R0 "/KEEP_APP_DATA" $R1
- ${If} ${Errors}
- RMDir /r "$APPDATA\${PRODUCT_FILENAME}"
- ${EndIf}
+ !ifmacrodef registerFileAssociations
+ !insertmacro registerFileAssociations
+ !endif
- !insertmacro MULTIUSER_RegistryRemoveInstallInfo
+ !ifmacrodef customInstall
+ !insertmacro customInstall
+ !endif
- !ifmacrodef customUnInstall
- !insertmacro customUnInstall
- !endif
-SectionEnd
\ No newline at end of file
+ ${IfNot} ${Silent}
+ !ifdef ONE_CLICK
+ # otherwise app window will be in backround
+ HideWindow
+ !ifdef RUN_AFTER_FINISH
+ Call StartApp
+ !endif
+ !endif
+ ${EndIf}
+ SectionEnd
+!else
+ Section
+ SectionEnd
+ !include "uninstaller.nsh"
+!endif
\ No newline at end of file
diff --git a/templates/nsis/langs.nsh b/templates/nsis/langs.nsh
new file mode 100644
index 00000000000..8ae0261a008
--- /dev/null
+++ b/templates/nsis/langs.nsh
@@ -0,0 +1,64 @@
+!insertmacro MUI_LANGUAGE "English"
+!insertmacro MUI_LANGUAGE "German"
+!insertmacro MUI_LANGUAGE "French"
+!insertmacro MUI_LANGUAGE "Spanish"
+!insertmacro MUI_LANGUAGE "SpanishInternational"
+!insertmacro MUI_LANGUAGE "SimpChinese"
+!insertmacro MUI_LANGUAGE "TradChinese"
+!insertmacro MUI_LANGUAGE "Japanese"
+#!insertmacro MUI_LANGUAGE "Korean"
+!insertmacro MUI_LANGUAGE "Italian"
+!insertmacro MUI_LANGUAGE "Dutch"
+#!insertmacro MUI_LANGUAGE "Danish"
+#!insertmacro MUI_LANGUAGE "Swedish"
+#!insertmacro MUI_LANGUAGE "Norwegian"
+#!insertmacro MUI_LANGUAGE "NorwegianNynorsk"
+#!insertmacro MUI_LANGUAGE "Finnish"
+#!insertmacro MUI_LANGUAGE "Greek"
+!insertmacro MUI_LANGUAGE "Russian"
+#!insertmacro MUI_LANGUAGE "Portuguese"
+#!insertmacro MUI_LANGUAGE "PortugueseBR"
+#!insertmacro MUI_LANGUAGE "Polish"
+#!insertmacro MUI_LANGUAGE "Ukrainian"
+!insertmacro MUI_LANGUAGE "Czech"
+#!insertmacro MUI_LANGUAGE "Slovak"
+#!insertmacro MUI_LANGUAGE "Croatian"
+#!insertmacro MUI_LANGUAGE "Bulgarian"
+#!insertmacro MUI_LANGUAGE "Hungarian"
+#!insertmacro MUI_LANGUAGE "Thai"
+#!insertmacro MUI_LANGUAGE "Romanian"
+#!insertmacro MUI_LANGUAGE "Latvian"
+#!insertmacro MUI_LANGUAGE "Macedonian"
+#!insertmacro MUI_LANGUAGE "Estonian"
+#!insertmacro MUI_LANGUAGE "Turkish"
+#!insertmacro MUI_LANGUAGE "Lithuanian"
+#!insertmacro MUI_LANGUAGE "Slovenian"
+#!insertmacro MUI_LANGUAGE "Serbian"
+#!insertmacro MUI_LANGUAGE "SerbianLatin"
+#!insertmacro MUI_LANGUAGE "Arabic"
+#!insertmacro MUI_LANGUAGE "Farsi"
+#!insertmacro MUI_LANGUAGE "Hebrew"
+#!insertmacro MUI_LANGUAGE "Indonesian"
+#!insertmacro MUI_LANGUAGE "Mongolian"
+#!insertmacro MUI_LANGUAGE "Luxembourgish"
+#!insertmacro MUI_LANGUAGE "Albanian"
+#!insertmacro MUI_LANGUAGE "Breton"
+#!insertmacro MUI_LANGUAGE "Belarusian"
+#!insertmacro MUI_LANGUAGE "Icelandic"
+#!insertmacro MUI_LANGUAGE "Malay"
+#!insertmacro MUI_LANGUAGE "Bosnian"
+#!insertmacro MUI_LANGUAGE "Kurdish"
+#!insertmacro MUI_LANGUAGE "Irish"
+#!insertmacro MUI_LANGUAGE "Uzbek"
+#!insertmacro MUI_LANGUAGE "Galician"
+#!insertmacro MUI_LANGUAGE "Afrikaans"
+#!insertmacro MUI_LANGUAGE "Catalan"
+#!insertmacro MUI_LANGUAGE "Esperanto"
+#!insertmacro MUI_LANGUAGE "Asturian"
+#!insertmacro MUI_LANGUAGE "Basque"
+#!insertmacro MUI_LANGUAGE "Pashto"
+#!insertmacro MUI_LANGUAGE "ScotsGaelic"
+#!insertmacro MUI_LANGUAGE "Georgian"
+#!insertmacro MUI_LANGUAGE "Vietnamese"
+#!insertmacro MUI_LANGUAGE "Welsh"
+#!insertmacro MUI_LANGUAGE "Armenian"
\ No newline at end of file
diff --git a/templates/nsis/multiUser.nsh b/templates/nsis/multiUser.nsh
index 71b72f1c3be..551a27f8b07 100644
--- a/templates/nsis/multiUser.nsh
+++ b/templates/nsis/multiUser.nsh
@@ -3,36 +3,24 @@
!define FOLDERID_UserProgramFiles {5CD7AEE2-2219-4A67-B85D-6C9CE15660CB}
!define KF_FLAG_CREATE 0x00008000
-!ifdef UNINSTALL_FILENAME & VERSION & APP_EXECUTABLE_FILENAME & PRODUCT_NAME & COMPANY_NAME & PRODUCT_FILENAME
-!else
- !error "Should define all variables: UNINSTALL_FILENAME & VERSION & APP_EXECUTABLE_FILENAME & PRODUCT_NAME & COMPANY_NAME & PRODUCT_FILENAME"
-!endif
-
!define INSTALL_REGISTRY_KEY "Software\${APP_GUID}"
!define UNINSTALL_REGISTRY_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_GUID}"
+!define UNINSTALL_DISPLAY_NAME "${PRODUCT_NAME} ${VERSION}"
-!ifndef MULTIUSER_INSTALLMODE_DISPLAYNAME
- !define MULTIUSER_INSTALLMODE_DISPLAYNAME "${PRODUCT_NAME} ${VERSION}"
-!endif
-
-# Current Install Mode ("AllUsers" or "CurrentUser")
-Var MultiUser.InstallMode
+# current Install Mode ("AllUsers" or "CurrentUser")
+Var installMode
!ifndef INSTALL_MODE_PER_ALL_USERS
!ifndef ONE_CLICK
- Var HasPerUserInstallation ; 0 (false) or 1 (true)
- Var HasPerMachineInstallation ; 0 (false) or 1 (true)
+ Var hasPerUserInstallation
+ Var hasPerMachineInstallation
!endif
Var PerUserInstallationFolder
- # Sets install mode to "per-user".
- !macro MULTIUSER_INSTALLMODE_CURRENTUSER UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX
- StrCpy $MultiUser.InstallMode CurrentUser
-
+ !macro setInstallModePerUser
+ StrCpy $installMode CurrentUser
SetShellVarContext current
-
- !if "${UNINSTALLER_PREFIX}" != UN
- # http://www.mathiaswestin.net/2012/09/how-to-make-per-user-installation-with.html
+ !ifndef BUILD_UNINSTALLER
StrCpy $0 "$LocalAppData\Programs"
# Win7 has a per-user programfiles known folder and this can be a non-default location
System::Call 'Shell32::SHGetKnownFolderPath(g "${FOLDERID_UserProgramFiles}",i ${KF_FLAG_CREATE},i0,*i.r2)i.r1'
@@ -41,140 +29,62 @@ Var MultiUser.InstallMode
StrCpy $0 $1
System::Call 'Ole32::CoTaskMemFree(ir2)'
${EndIf}
- StrCpy $Instdir "$0\${PRODUCT_FILENAME}"
+ StrCpy $INSTDIR "$0\${PRODUCT_FILENAME}"
!endif
- # Checks registry for previous installation path (both for upgrading, reinstall, or uninstall)
- ReadRegStr $PerUserInstallationFolder HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation
- ${if} $PerUserInstallationFolder != ""
- StrCpy $INSTDIR $PerUserInstallationFolder
+ # сhecks registry for previous installation path (both for upgrading, reinstall, or uninstall)
+ ReadRegStr $perUserInstallationFolder HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation
+ ${if} $perUserInstallationFolder != ""
+ StrCpy $INSTDIR $perUserInstallationFolder
${endif}
-
- !ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION
- Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}"
- !endif
!macroend
- Function MultiUser.InstallMode.CurrentUser
- !insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER "" ""
- FunctionEnd
-
- Function un.MultiUser.InstallMode.CurrentUser
- !insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER UN un.
- FunctionEnd
+ !ifndef BUILD_UNINSTALLER
+ Function installMode.CurrentUser
+ !insertmacro setInstallModePerUser
+ FunctionEnd
+ !endif
!endif
!ifdef INSTALL_MODE_PER_ALL_USERS_REQUIRED
- Var PerMachineInstallationFolder
+ Var perMachineInstallationFolder
# Sets install mode to "per-machine" (all users).
- !macro MULTIUSER_INSTALLMODE_ALLUSERS UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX
+ !macro setInstallModePerAllUsers
# Install mode initialization - per-machine
- StrCpy $MultiUser.InstallMode AllUsers
+ StrCpy $installMode AllUsers
SetShellVarContext all
- !if "${UNINSTALLER_PREFIX}" != UN
- ;Set default installation location for installer
+ !ifndef BUILD_UNINSTALLER
StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCT_FILENAME}"
!endif
- ; Checks registry for previous installation path (both for upgrading, reinstall, or uninstall)
- ReadRegStr $PerMachineInstallationFolder HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation
- ${if} $PerMachineInstallationFolder != ""
- StrCpy $INSTDIR $PerMachineInstallationFolder
- ${endif}
-
- !ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION
- Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}"
- !endif
- !macroend
-
- Function MultiUser.InstallMode.AllUsers
- !insertmacro MULTIUSER_INSTALLMODE_ALLUSERS "" ""
- FunctionEnd
-
- Function un.MultiUser.InstallMode.AllUsers
- !insertmacro MULTIUSER_INSTALLMODE_ALLUSERS UN un.
- FunctionEnd
-!endif
-
-!macro MULTIUSER_INIT_TEXTS
- !ifndef MULTIUSER_INIT_TEXT_ADMINREQUIRED
- !define MULTIUSER_INIT_TEXT_ADMINREQUIRED "$(^Caption) requires administrator privileges."
- !endif
-
- !ifndef MULTIUSER_INIT_TEXT_POWERREQUIRED
- !define MULTIUSER_INIT_TEXT_POWERREQUIRED "$(^Caption) requires at least Power User privileges."
- !endif
-
- !ifndef MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE
- !define MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE "Your user account does not have sufficient privileges to install $(^Name) for all users of this computer."
- !endif
-!macroend
-
-!macro initMultiUser UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX
- !ifdef ONE_CLICK
- !ifdef INSTALL_MODE_PER_ALL_USERS
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
- !else
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
- !endif
- !else
- !insertmacro UAC_PageElevation_OnInit
-
- ${If} ${UAC_IsInnerInstance}
- ${AndIfNot} ${UAC_IsAdmin}
- # special return value for outer instance so it knows we did not have admin rights
- SetErrorLevel 0x666666
- Quit
- ${EndIf}
-
- !insertmacro MULTIUSER_INIT_TEXTS
-
# checks registry for previous installation path (both for upgrading, reinstall, or uninstall)
- StrCpy $HasPerMachineInstallation "0"
- StrCpy $HasPerUserInstallation "0"
-
- # set installation mode to setting from a previous installation
- ReadRegStr $PerMachineInstallationFolder HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation
- ${if} $PerMachineInstallationFolder != ""
- StrCpy $HasPerMachineInstallation "1"
- ${endif}
-
- ReadRegStr $PerUserInstallationFolder HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation
- ${if} $PerUserInstallationFolder != ""
- StrCpy $HasPerUserInstallation "1"
+ ReadRegStr $perMachineInstallationFolder HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation
+ ${if} $perMachineInstallationFolder != ""
+ StrCpy $INSTDIR $perMachineInstallationFolder
${endif}
+ !macroend
- ${if} $HasPerUserInstallation == "1"
- ${andif} $HasPerMachineInstallation == "0"
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
- ${elseif} $HasPerUserInstallation == "0"
- ${andif} $HasPerMachineInstallation == "1"
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
- ${else}
- # if there is no installation, or there is both per-user and per-machine
- !ifdef INSTALL_MODE_PER_ALL_USERS
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
- !else
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
- !endif
- ${endif}
+ !ifndef BUILD_UNINSTALLER
+ Function installMode.AllUsers
+ !insertmacro setInstallModePerAllUsers
+ FunctionEnd
!endif
-!macroend
+!endif
# SHCTX is the hive HKLM if SetShellVarContext all, or HKCU if SetShellVarContext user
-!macro MULTIUSER_RegistryAddInstallInfo
+!macro registryAddInstallInfo
# Write the installation path into the registry
WriteRegStr SHCTX "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR"
# Write the uninstall keys for Windows
- ${if} $MultiUser.InstallMode == "AllUsers"
- WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" DisplayName "${MULTIUSER_INSTALLMODE_DISPLAYNAME}"
+ ${if} $installMode == "AllUsers"
+ WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" DisplayName "${UNINSTALL_DISPLAY_NAME}"
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" UninstallString '"$INSTDIR\${UNINSTALL_FILENAME}" /allusers'
${else}
- WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" DisplayName "${MULTIUSER_INSTALLMODE_DISPLAYNAME} (only current user)"
+ WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" DisplayName "${UNINSTALL_DISPLAY_NAME} (only current user)"
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" UninstallString '"$INSTDIR\${UNINSTALL_FILENAME}" /currentuser'
${endif}
@@ -187,9 +97,4 @@ Var MultiUser.InstallMode
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD SHCTX "${UNINSTALL_REGISTRY_KEY}" "EstimatedSize" "$0"
-!macroend
-
-!macro MULTIUSER_RegistryRemoveInstallInfo
- DeleteRegKey SHCTX "${UNINSTALL_REGISTRY_KEY}"
- DeleteRegKey SHCTX "${INSTALL_REGISTRY_KEY}"
!macroend
\ No newline at end of file
diff --git a/templates/nsis/multiUserUi.nsh b/templates/nsis/multiUserUi.nsh
index 76d4138a028..98c702509b7 100755
--- a/templates/nsis/multiUserUi.nsh
+++ b/templates/nsis/multiUserUi.nsh
@@ -3,82 +3,67 @@
RequestExecutionLevel user
-Var HasTwoAvailableOptions ; 0 (false) or 1 (true)
+Var HasTwoAvailableOptions
Var RadioButtonLabel1
-!macro MULTIUSER_INIT_QUIT UNINSTALLER_FUNCPREFIX
- !ifdef MULTIUSER_INIT_${UNINSTALLER_FUNCPREFIX}FUNCTIONQUIT
- Call "${MULTIUSER_INIT_${UNINSTALLER_FUNCPREFIX}FUCTIONQUIT}"
- !else
- Quit
- !endif
-!macroend
-
-!macro MULTIUSER_INSTALLMODEPAGE_INTERFACE
- Var MultiUser.InstallModePage
- Var MultiUser.InstallModePage.Text
- Var MultiUser.InstallModePage.AllUsers
- Var MultiUser.InstallModePage.CurrentUser
- Var MultiUser.InstallModePage.ReturnValue
-!macroend
-
-!macro MULTIUSER_PAGEDECLARATION_INSTALLMODE
- !insertmacro MUI_SET MULTIUSER_${MUI_PAGE_UNINSTALLER_PREFIX}INSTALLMODEPAGE ""
- !insertmacro MULTIUSER_INSTALLMODEPAGE_INTERFACE
- !insertmacro MULTIUSER_FUNCTION_INSTALLMODEPAGE MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID} "" ""
- !insertmacro MULTIUSER_FUNCTION_INSTALLMODEPAGE MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID} UN un.
-
- PageEx custom
- PageCallbacks MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID}
- Caption " "
- PageExEnd
-
- UninstPage custom un.MultiUser.InstallModePre_${MUI_UNIQUEID} un.MultiUser.InstallModeLeave_${MUI_UNIQUEID}
-!macroend
-
-!macro MULTIUSER_PAGE_INSTALLMODE
+!macro PAGE_INSTALL_MODE
!insertmacro MUI_PAGE_INIT
- !insertmacro MULTIUSER_PAGEDECLARATION_INSTALLMODE
+
+ !insertmacro MUI_SET MULTIUSER_${MUI_PAGE_UNINSTALLER_PREFIX}INSTALLMODEPAGE ""
+ Var MultiUser.InstallModePage
+ Var MultiUser.InstallModePage.Text
+ Var MultiUser.InstallModePage.AllUsers
+ Var MultiUser.InstallModePage.CurrentUser
+ Var MultiUser.InstallModePage.ReturnValue
+
+ !ifndef BUILD_UNINSTALLER
+ !insertmacro FUNCTION_INSTALL_MODE_PAGE_FUNCTION MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID} ""
+ PageEx custom
+ PageCallbacks MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID}
+ Caption " "
+ PageExEnd
+ !else
+ !insertmacro FUNCTION_INSTALL_MODE_PAGE_FUNCTION MultiUser.InstallModePre_${MUI_UNIQUEID} MultiUser.InstallModeLeave_${MUI_UNIQUEID} un.
+ UninstPage custom un.multiUser.InstallModePre_${MUI_UNIQUEID} un.MultiUser.InstallModeLeave_${MUI_UNIQUEID}
+ !endif
!macroend
-!macro MULTIUSER_FUNCTION_INSTALLMODEPAGE PRE LEAVE UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX
+!macro FUNCTION_INSTALL_MODE_PAGE_FUNCTION PRE LEAVE UNINSTALLER_FUNCPREFIX
Function "${UNINSTALLER_FUNCPREFIX}${PRE}"
${If} ${UAC_IsInnerInstance}
${AndIf} ${UAC_IsAdmin}
- # Inner Process (and Admin) - skip selection, inner process is always used for elevation (machine-wide)
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
+ # inner Process (and Admin) - skip selection, inner process is always used for elevation (machine-wide)
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.AllUsers
Abort
${EndIf}
${GetParameters} $R0
${GetOptions} $R0 "/allusers" $R1
${IfNot} ${Errors}
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.AllUsers
Abort
${EndIf}
${GetOptions} $R0 "/currentuser" $R1
${IfNot} ${Errors}
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.CurrentUser
Abort
${EndIf}
# If uninstalling, will check if there is both a per-user and per-machine installation. If there is only one, will skip the form.
# If uninstallation was invoked from the "add/remove programs" Windows will automatically requests elevation (depending if uninstall keys are in HKLM or HKCU)
# so (for uninstallation) just checking UAC_IsAdmin would probably be enought to determine if it's a per-user or per-machine. However, user can run the uninstall.exe from the folder itself
- !if "${UNINSTALLER_PREFIX}" == UN
- ${if} $HasPerUserInstallation == "1"
- ${andif} $HasPerMachineInstallation == "0"
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
+ !ifdef BUILD_UNINSTALLER
+ ${if} $hasPerUserInstallation == "1"
+ ${andif} $hasPerMachineInstallation == "0"
+ Call un.installMode.CurrentUser
Abort
- ${elseif} $HasPerUserInstallation == "0"
- ${andif} $HasPerMachineInstallation == "1"
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
+ ${elseif} $hasPerUserInstallation == "0"
+ ${andif} $hasPerMachineInstallation == "1"
+ Call un.installMode.AllUsers
Abort
${endif}
- !endif
- !if "${UNINSTALLER_PREFIX}" == UN
!insertmacro MUI_HEADER_TEXT "Choose Uninstallation Options" "Which installation should be removed?"
!else
!insertmacro MUI_HEADER_TEXT "Choose Installation Options" "Who should this application be installed for?"
@@ -87,7 +72,7 @@ Var RadioButtonLabel1
nsDialogs::Create 1018
Pop $MultiUser.InstallModePage
- !if "${UNINSTALLER_PREFIX}" != UN
+ !ifndef BUILD_UNINSTALLER
${NSD_CreateLabel} 0u 0u 300u 20u "Please select whether you wish to make this software available to all users or just yourself"
StrCpy $8 "Anyone who uses this computer (&all users)"
StrCpy $9 "Only for &me"
@@ -128,7 +113,7 @@ Var RadioButtonLabel1
${NSD_CreateLabel} 0u 110u 280u 50u ""
Pop $RadioButtonLabel1
- ${if} $MultiUser.InstallMode == "AllUsers" ; setting defaults
+ ${if} $installMode == "AllUsers" ; setting defaults
SendMessage $MultiUser.InstallModePage.AllUsers ${BM_SETCHECK} ${BST_CHECKED} 0 ; set as default
SendMessage $MultiUser.InstallModePage.AllUsers ${BM_CLICK} 0 0 ; trigger click event
${else}
@@ -145,7 +130,7 @@ Var RadioButtonLabel1
${if} $MultiUser.InstallModePage.ReturnValue = ${BST_CHECKED}
${if} ${UAC_IsAdmin}
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.AllUsers
${else}
!ifdef MULTIUSER_INSTALLMODE_ALLOW_ELEVATION
GetDlgItem $9 $HWNDParent 1
@@ -173,7 +158,7 @@ Var RadioButtonLabel1
!endif
${endif}
${else}
- Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
+ Call ${UNINSTALLER_FUNCPREFIX}installMode.CurrentUser
${endif}
!insertmacro MUI_PAGE_FUNCTION_CUSTOM LEAVE
@@ -187,22 +172,22 @@ Var RadioButtonLabel1
StrCpy $7 ""
${if} "$1" == "0" ; current user
- ${if} $HasPerUserInstallation == "1"
- !if "${UNINSTALLER_PREFIX}" != UN
- StrCpy $7 "There is already a per-user installation. ($PerUserInstallationFolder)$\r$\nWill reinstall/upgrade."
+ ${if} $hasPerUserInstallation == "1"
+ !ifndef BUILD_UNINSTALLER
+ StrCpy $7 "There is already a per-user installation. ($perUserInstallationFolder)$\r$\nWill reinstall/upgrade."
!else
- StrCpy $7 "There is a per-user installation. ($PerUserInstallationFolder)$\r$\nWill uninstall."
+ StrCpy $7 "There is a per-user installation. ($perUserInstallationFolder)$\r$\nWill uninstall."
!endif
${else}
StrCpy $7 "Fresh install for current user only"
${endif}
SendMessage $0 ${BCM_SETSHIELD} 0 0 ; hide SHIELD
${else} ; all users
- ${if} $HasPerMachineInstallation == "1"
- !if "${UNINSTALLER_PREFIX}" != UN
- StrCpy $7 "There is already a per-machine installation. ($PerMachineInstallationFolder)$\r$\nWill reinstall/upgrade."
+ ${if} $hasPerMachineInstallation == "1"
+ !ifndef BUILD_UNINSTALLER
+ StrCpy $7 "There is already a per-machine installation. ($perMachineInstallationFolder)$\r$\nWill reinstall/upgrade."
!else
- StrCpy $7 "There is a per-machine installation. ($PerMachineInstallationFolder)$\r$\nWill uninstall."
+ StrCpy $7 "There is a per-machine installation. ($perMachineInstallationFolder)$\r$\nWill uninstall."
!endif
${else}
StrCpy $7 "Fresh install for all users"
diff --git a/templates/nsis/oneClick.nsh b/templates/nsis/oneClick.nsh
index 387457595aa..47c47a525b5 100644
--- a/templates/nsis/oneClick.nsh
+++ b/templates/nsis/oneClick.nsh
@@ -1,17 +1,21 @@
!ifdef RUN_AFTER_FINISH
- Function StartApp
- !ifdef INSTALL_MODE_PER_ALL_USERS
- !include UAC.nsh
- !insertmacro UAC_AsUser_ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk" "" "" ""
- !else
- ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
- !endif
- FunctionEnd
+ !ifndef BUILD_UNINSTALLER
+ Function StartApp
+ !ifdef INSTALL_MODE_PER_ALL_USERS
+ !include UAC.nsh
+ !insertmacro UAC_AsUser_ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk" "" "" ""
+ !else
+ ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
+ !endif
+ FunctionEnd
+ !endif
!endif
AutoCloseWindow true
!insertmacro MUI_PAGE_INSTFILES
-!insertmacro MUI_UNPAGE_INSTFILES
+!ifdef BUILD_UNINSTALLER
+ !insertmacro MUI_UNPAGE_INSTFILES
+!endif
!insertmacro MUI_LANGUAGE "English"
@@ -19,4 +23,20 @@ AutoCloseWindow true
RequestExecutionLevel admin
!else
RequestExecutionLevel user
-!endif
\ No newline at end of file
+!endif
+
+!macro initMultiUser UNINSTALLER_FUNCPREFIX
+ !ifdef INSTALL_MODE_PER_ALL_USERS
+ !ifdef BUILD_UNINSTALLER
+ Call un.installMode.AllUsers
+ !else
+ Call installMode.AllUsers
+ !endif
+ !else
+ !ifdef BUILD_UNINSTALLER
+ Call un.installMode.CurrentUser
+ !else
+ Call installMode.CurrentUser
+ !endif
+ !endif
+!macroend
\ No newline at end of file
diff --git a/templates/nsis/uninstaller.nsh b/templates/nsis/uninstaller.nsh
new file mode 100644
index 00000000000..7710bb305cf
--- /dev/null
+++ b/templates/nsis/uninstaller.nsh
@@ -0,0 +1,71 @@
+!ifndef INSTALL_MODE_PER_ALL_USERS
+ Function un.installMode.CurrentUser
+ !insertmacro setInstallModePerUser
+ FunctionEnd
+!endif
+
+!ifdef INSTALL_MODE_PER_ALL_USERS_REQUIRED
+ Function un.installMode.AllUsers
+ !insertmacro setInstallModePerAllUsers
+ FunctionEnd
+!endif
+
+Function un.onInit
+ !insertmacro check64BitAndSetRegView
+
+ ${IfNot} ${Silent}
+ MessageBox MB_OKCANCEL "Are you sure you want to uninstall ${PRODUCT_NAME}?" IDOK +2
+ Quit
+
+ !ifdef ONE_CLICK
+ # one-click installer executes uninstall section in the silent mode, but we must show message dialog if silent mode was not explicitly set by user (using /S flag)
+ !insertmacro CHECK_APP_RUNNING "uninstall"
+ SetSilent silent
+ !endif
+ ${EndIf}
+
+ !insertmacro initMultiUser un.
+
+ !ifmacrodef customUnInit
+ !insertmacro customUnInit
+ !endif
+FunctionEnd
+
+Section "un.install"
+ SetAutoClose true
+
+ !ifndef ONE_CLICK
+ # for boring installer we check it here to show progress
+ !insertmacro CHECK_APP_RUNNING "uninstall"
+ !endif
+
+ StrCpy $startMenuLink "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
+ StrCpy $desktopLink "$DESKTOP\${PRODUCT_FILENAME}.lnk"
+
+ WinShell::UninstAppUserModelId "${APP_ID}"
+ WinShell::UninstShortcut "$startMenuLink"
+ WinShell::UninstShortcut "$desktopLink"
+
+ Delete "$startMenuLink"
+ Delete "$desktopLink"
+
+ !ifmacrodef unregisterFileAssociations
+ !insertmacro unregisterFileAssociations
+ !endif
+
+ # delete the installed files
+ RMDir /r $INSTDIR
+
+ ${GetParameters} $R0
+ ${GetOptions} $R0 "/KEEP_APP_DATA" $R1
+ ${If} ${Errors}
+ RMDir /r "$APPDATA\${PRODUCT_FILENAME}"
+ ${EndIf}
+
+ DeleteRegKey SHCTX "${UNINSTALL_REGISTRY_KEY}"
+ DeleteRegKey SHCTX "${INSTALL_REGISTRY_KEY}"
+
+ !ifmacrodef customUnInstall
+ !insertmacro customUnInstall
+ !endif
+SectionEnd
\ No newline at end of file
diff --git a/test/src/helpers/packTester.ts b/test/src/helpers/packTester.ts
index 9e0ecba1d31..1e32bdd80ff 100755
--- a/test/src/helpers/packTester.ts
+++ b/test/src/helpers/packTester.ts
@@ -34,20 +34,29 @@ interface AssertPackOptions {
readonly expectedDepends?: string
readonly useTempDir?: boolean
+ readonly signed?: boolean
readonly npmInstallBefore?: boolean
}
-export async function assertPack(fixtureName: string, packagerOptions: PackagerOptions, checkOptions?: AssertPackOptions): Promise {
- const tempDirCreated = checkOptions == null ? null : checkOptions.tempDirCreated
- const useTempDir = fixtureName !== "app-executable-deps" && (tempDirCreated != null || packagerOptions.devMetadata != null || (checkOptions != null && checkOptions.useTempDir) || packagerOptions.targets.values().next().value.values().next().value[0] !== DEFAULT_TARGET)
+export function app(packagerOptions: PackagerOptions, checkOptions: AssertPackOptions = {}) {
+ return () => assertPack("test-app-one", packagerOptions, checkOptions)
+}
+
+export async function assertPack(fixtureName: string, packagerOptions: PackagerOptions, checkOptions: AssertPackOptions = {}): Promise {
+ if (checkOptions.signed) {
+ packagerOptions = signed(packagerOptions)
+ }
+
+ const tempDirCreated = checkOptions.tempDirCreated
+ const useTempDir = fixtureName !== "app-executable-deps" && (tempDirCreated != null || packagerOptions.devMetadata != null || checkOptions.useTempDir || packagerOptions.targets.values().next().value.values().next().value[0] !== DEFAULT_TARGET)
let projectDir = path.join(__dirname, "..", "..", "fixtures", fixtureName)
// const isDoNotUseTempDir = platform === "darwin"
const customTmpDir = process.env.TEST_APP_TMP_DIR
if (useTempDir) {
// non-osx test uses the same dir as osx test, but we cannot share node_modules (because tests executed in parallel)
- const dir = customTmpDir == null ? path.join(tmpdir(), `${getTempName("electron-builder-test")}`) : path.resolve(customTmpDir)
+ const dir = customTmpDir == null ? path.join(tmpdir(), "electron-builder-test", `${getTempName()}`) : path.resolve(customTmpDir)
if (customTmpDir != null) {
log(`Custom temp dir used: ${customTmpDir}`)
}
@@ -64,7 +73,7 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO
try {
if (tempDirCreated != null) {
await tempDirCreated(projectDir)
- if (checkOptions != null && checkOptions.npmInstallBefore) {
+ if (checkOptions.npmInstallBefore) {
await spawnNpmProduction("install", projectDir)
}
}
@@ -73,7 +82,7 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO
projectDir: projectDir,
}, packagerOptions), checkOptions)
- if (checkOptions != null && checkOptions.packed != null) {
+ if (checkOptions.packed != null) {
await checkOptions.packed(projectDir)
}
}
@@ -83,7 +92,7 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO
await remove(projectDir)
}
catch (e) {
- console.warn("Cannot delete temporary directory " + projectDir + ": " + (e.stack || e))
+ console.warn(`Cannot delete temporary directory ${projectDir}: ${(e.stack || e)}`)
}
}
}
diff --git a/test/src/helpers/runTests.ts b/test/src/helpers/runTests.ts
index c796637ab3c..5df322c6c22 100755
--- a/test/src/helpers/runTests.ts
+++ b/test/src/helpers/runTests.ts
@@ -1,9 +1,9 @@
import { spawn } from "child_process"
import * as path from "path"
import { Promise as BluebirdPromise } from "bluebird"
-import { copy, emptyDir, outputFile, readdir, readFileSync, readJson, unlink } from "fs-extra-p"
+import { copy, emptyDir, outputFile, readdir, readFileSync, readJson, unlink, remove } from "fs-extra-p"
import { Platform } from "out/metadata"
-import { cpus, homedir } from "os"
+import { cpus, homedir, tmpdir } from "os"
// we set NODE_PATH in this file, so, we cannot use 'out/awaiter' path here
//noinspection JSUnusedLocalSymbols
@@ -17,15 +17,17 @@ const downloadElectron: (options: any) => Promise = BluebirdPromise.promisi
const packager = require("../../../out/packager")
const rootDir = path.join(__dirname, "..", "..", "..")
-const testPackageDir = path.join(require("os").tmpdir(), "electron_builder_published")
+const testPackageDir = path.join(tmpdir(), "electron_builder_published")
const testNodeModules = path.join(testPackageDir, "node_modules")
const electronVersion = "1.3.2"
async function main() {
+ const tempTestBaseDir = path.join(tmpdir(), "electron-builder-test")
await BluebirdPromise.all([
deleteOldElectronVersion(),
downloadAllRequiredElectronVersions(),
+ emptyDir(tempTestBaseDir),
outputFile(path.join(testPackageDir, "package.json"), `{
"private": true,
"version": "1.0.0",
@@ -42,7 +44,12 @@ async function main() {
await exec(["install", "--cache-min", "999999999", "--production", rootDir])
// prune stale packages
await exec(["prune", "--production"])
- await runTests()
+ try {
+ await runTests()
+ }
+ finally {
+ await remove(tempTestBaseDir)
+ }
}
main()
diff --git a/test/src/nsisTest.ts b/test/src/nsisTest.ts
index 64fc15f9f39..56940fccf9a 100644
--- a/test/src/nsisTest.ts
+++ b/test/src/nsisTest.ts
@@ -1,6 +1,6 @@
import { Platform, Arch } from "out"
import test from "./helpers/avaEx"
-import { assertPack, signed, getTestAsset } from "./helpers/packTester"
+import { assertPack, getTestAsset, app } from "./helpers/packTester"
import { copy } from "fs-extra-p"
import * as path from "path"
import { Promise as BluebirdPromise } from "bluebird"
@@ -9,14 +9,10 @@ import { assertThat } from "./helpers/fileAssert"
//noinspection JSUnusedLocalSymbols
const __awaiter = require("out/util/awaiter")
-test("one-click", () => assertPack("test-app-one", signed({
- targets: Platform.WINDOWS.createTarget(["nsis"]),
- }), {
- useTempDir: true,
- }
-))
+const nsisTarget = Platform.WINDOWS.createTarget(["nsis"])
+test("one-click", app({targets: nsisTarget}, {useTempDir: true, signed: true}))
-test.ifDevOrLinuxCi("perMachine, no run after finish", () => assertPack("test-app-one", {
+test.ifDevOrLinuxCi("perMachine, no run after finish", app({
targets: Platform.WINDOWS.createTarget(["nsis"], Arch.ia32, Arch.x64),
devMetadata: {
build: {
@@ -34,8 +30,8 @@ test.ifDevOrLinuxCi("perMachine, no run after finish", () => assertPack("test-ap
}
}))
-test.ifNotCiOsx("boring", () => assertPack("test-app-one", signed({
- targets: Platform.WINDOWS.createTarget(["nsis"]),
+test.ifNotCiOsx("boring", app({
+ targets: nsisTarget,
devMetadata: {
build: {
nsis: {
@@ -43,12 +39,12 @@ test.ifNotCiOsx("boring", () => assertPack("test-app-one", signed({
}
}
}
-})))
+}, {signed: true}))
test.ifNotCiOsx("installerHeaderIcon", () => {
let headerIconPath: string | null = null
return assertPack("test-app-one", {
- targets: Platform.WINDOWS.createTarget(["nsis"]),
+ targets: nsisTarget,
effectiveOptionComputed: options => {
const defines = options[0]
assertThat(defines.HEADER_ICO).isEqualTo(headerIconPath)
@@ -66,7 +62,7 @@ test.ifNotCiOsx("installerHeaderIcon", () => {
test.ifNotCiOsx("boring, MUI_HEADER", () => {
let installerHeaderPath: string | null = null
return assertPack("test-app-one", {
- targets: Platform.WINDOWS.createTarget(["nsis"]),
+ targets: nsisTarget,
devMetadata: {
build: {
nsis: {
@@ -94,7 +90,7 @@ test.ifNotCiOsx("boring, MUI_HEADER", () => {
test.ifNotCiOsx("boring, MUI_HEADER as option", () => {
let installerHeaderPath: string | null = null
return assertPack("test-app-one", {
- targets: Platform.WINDOWS.createTarget(["nsis"]),
+ targets: nsisTarget,
devMetadata: {
build: {
nsis: {
@@ -120,9 +116,7 @@ test.ifNotCiOsx("boring, MUI_HEADER as option", () => {
)
})
-test.ifDevOrLinuxCi("custom include", () => assertPack("test-app-one", {
- targets: Platform.WINDOWS.createTarget(["nsis"]),
-}, {
+test.ifDevOrLinuxCi("custom include", () => assertPack("test-app-one", {targets: nsisTarget}, {
tempDirCreated: projectDir => copy(getTestAsset("installer.nsh"), path.join(projectDir, "build", "installer.nsh")),
packed: projectDir => BluebirdPromise.all([
assertThat(path.join(projectDir, "build", "customHeader")).isFile(),
@@ -131,9 +125,7 @@ test.ifDevOrLinuxCi("custom include", () => assertPack("test-app-one", {
]),
}))
-test.ifDevOrLinuxCi("custom script", () => assertPack("test-app-one", {
- targets: Platform.WINDOWS.createTarget(["nsis"]),
-}, {
+test.ifDevOrLinuxCi("custom script", app({targets: nsisTarget}, {
tempDirCreated: projectDir => copy(getTestAsset("installer.nsi"), path.join(projectDir, "build", "installer.nsi")),
packed: projectDir => assertThat(path.join(projectDir, "build", "customInstallerScript")).isFile(),
}))
\ No newline at end of file
diff --git a/test/src/winPackagerTest.ts b/test/src/winPackagerTest.ts
index 5c14c97cd09..75be6abc410 100755
--- a/test/src/winPackagerTest.ts
+++ b/test/src/winPackagerTest.ts
@@ -1,6 +1,6 @@
import { Platform, Arch, BuildInfo } from "out"
import test from "./helpers/avaEx"
-import { assertPack, platform, modifyPackageJson, signed, getTestAsset } from "./helpers/packTester"
+import { assertPack, platform, modifyPackageJson, getTestAsset, app } from "./helpers/packTester"
import { outputFile, rename, copy } from "fs-extra-p"
import * as path from "path"
import { WinPackager } from "out/winPackager"
@@ -14,24 +14,20 @@ import { ElectronPackagerOptions } from "out/packager/dirPackager"
//noinspection JSUnusedLocalSymbols
const __awaiter = require("out/util/awaiter")
-test.ifNotCiOsx("win", () => assertPack("test-app-one", signed({
- targets: Platform.WINDOWS.createTarget(["default", "zip"]),
- })
-))
+test.ifNotCiOsx("win", app({targets: Platform.WINDOWS.createTarget(["default", "zip"])}, {signed: true}))
// very slow
-test.skip("delta and msi", () => assertPack("test-app-one", {
- targets: Platform.WINDOWS.createTarget(null, Arch.ia32),
- devMetadata: {
- build: {
- win: {
- remoteReleases: "https://github.com/develar/__test-app-releases",
- msi: true,
- },
- }
- },
- }
-))
+test.skip("delta and msi", app({
+ targets: Platform.WINDOWS.createTarget(null, Arch.ia32),
+ devMetadata: {
+ build: {
+ win: {
+ remoteReleases: "https://github.com/develar/__test-app-releases",
+ msi: true,
+ },
+ }
+ },
+}))
test.ifDevOrWinCi("beta version", () => {
const metadata: any = {
@@ -148,7 +144,7 @@ class CheckingWinPackager extends WinPackager {
this.effectivePackOptions = await this.computePackOptions()
const helperClass: typeof SquirrelWindowsTarget = require("out/targets/squirrelWindows").default
- this.effectiveDistOptions = await (new helperClass(this).computeEffectiveDistOptions())
+ this.effectiveDistOptions = await (new helperClass(this, []).computeEffectiveDistOptions())
await this.sign(this.computeAppOutDir(outDir, arch))
}