From d6f50b66d6fa72dac194c82dcd859214b01c503a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Jan 2016 17:09:21 -0800 Subject: [PATCH] add the inner/outer paradigm for signed uninstaller --- cmake/templates/NSIS.template.in | 68 +++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index c65c5d4dca..68f1776ab8 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -31,15 +31,41 @@ ;-------------------------------- ;General - ;Name and file - Name "@CPACK_NSIS_PACKAGE_NAME@" - OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" + !ifdef INNER + !echo "Inner invocation" ; just to see what's going on + OutFile "$%TEMP%\tempinstaller.exe" ; not really important where this is + SetCompress off ; for speed + !else + !echo "Outer invocation" - ;Set compression - SetCompressor @CPACK_NSIS_COMPRESSOR@ + ; Call makensis again, defining INNER. This writes an installer for us which, when + ; it is invoked, will just write the uninstaller to some location, and then exit. + ; Be sure to substitute the name of this script here. - ;Require administrator access - RequestExecutionLevel admin + !system "$\"${NSISDIR}\makensis$\" /DINNER .nsi" = 0 + + ; So now run that installer we just created as %TEMP%\tempinstaller.exe. Since it + ; calls quit the return value isn't zero. + + !system "$%TEMP%\tempinstaller.exe" = 2 + + ; That will have written an uninstaller binary for us. Now we sign it with your + ; favourite code signing tool. + + !system "signcode $%TEMP%\uninstaller.exe" = 0 + + ; Good. Now we can carry on writing the real installer. + + ;Name and file + Name "@CPACK_NSIS_PACKAGE_NAME@" + OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" + + ;Set compression + SetCompressor @CPACK_NSIS_COMPRESSOR@ + + ;Require administrator access + RequestExecutionLevel admin + !endif @CPACK_NSIS_DEFINES@ @@ -648,8 +674,15 @@ Section "-Core installation" ;Store installation folder WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR - ;Create uninstaller - WriteUninstaller "$INSTDIR\Uninstall.exe" + ;Package the signed uninstaller produced by the inner loop + !ifndef INNER + SetOutPath $INSTDIR + + ; this packages the signed uninstaller + + File $%TEMP%\uninstaller.exe + !endif + Push "DisplayName" Push "@CPACK_NSIS_DISPLAY_NAME@" Call ConditionalAddToRegisty @@ -951,6 +984,10 @@ FunctionEnd ;-------------------------------- ;Uninstaller Section +; the normal uninstaller section is only defined for inner (they're not needed in the "outer" +; installer and will just cause warnings because there is no WriteInstaller command) + +!ifdef INNER Section "Uninstall" ReadRegStr $START_MENU SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" @@ -1046,6 +1083,7 @@ Section "Uninstall" Call un.RemoveFromPath doNotRemoveFromPath: SectionEnd +!endif ;-------------------------------- ; determine admin versus local install @@ -1058,6 +1096,18 @@ SectionEnd ; "Program Files" for AllUsers, "My Documents" for JustMe... Function .onInit + + !ifdef INNER + ; If INNER is defined, then we aren't supposed to do anything except write out + ; the installer. This is better than processing a command line option as it means + ; this entire code path is not present in the final (real) installer. + + WriteUninstaller "$%TEMP%\uninstaller.exe" + + ; just bail out quickly when running the "inner" installer + Quit + !endif + StrCmp "@CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL@" "ON" 0 inst ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "UninstallString"