From 09475c298b83cbbaae2ca7ba6f52c0fa74607568 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20W=C3=BCger?= <office@wueger.at>
Date: Wed, 1 Mar 2017 20:18:28 +0100
Subject: [PATCH] CPack/NSIS: Sign the uninstaller

---
 Help/release/dev/cpack-sign-uninstaller.rst |  5 +++
 Modules/CPackNSIS.cmake                     |  4 ++
 Modules/NSIS.template.in                    | 45 ++++++++++++++++++++-
 3 files changed, 53 insertions(+), 1 deletion(-)
 create mode 100644 Help/release/dev/cpack-sign-uninstaller.rst

diff --git a/Help/release/dev/cpack-sign-uninstaller.rst b/Help/release/dev/cpack-sign-uninstaller.rst
new file mode 100644
index 0000000000..ff2b402d81
--- /dev/null
+++ b/Help/release/dev/cpack-sign-uninstaller.rst
@@ -0,0 +1,5 @@
+cpack-sign_uninstaller
+----------------------
+
+* The :module:`CPackNSIS` module learned to sign the uninstaller
+  when using :variable:`CPACK_NSIS_SIGN_UNINSTALLER` variable.
diff --git a/Modules/CPackNSIS.cmake b/Modules/CPackNSIS.cmake
index 18d1871571..37fedf335c 100644
--- a/Modules/CPackNSIS.cmake
+++ b/Modules/CPackNSIS.cmake
@@ -133,6 +133,10 @@
 #       "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/cmake.html"
 #       "CMake Help" "https://cmake.org" "CMake Web Site")
 #
+# .. variable:: CPACK_NSIS_SIGN_UNINSTALLER
+#
+#  Specify a command to use for signing the uninstaller.  The command will
+#  be invoked a path to the uninstaller as its only argument.
 
 #FIXME we should put NSIS specific code here
 #FIXME but I'm not doing it because I'm not able to test it...
diff --git a/Modules/NSIS.template.in b/Modules/NSIS.template.in
index 9001888da1..ffa62771bf 100644
--- a/Modules/NSIS.template.in
+++ b/Modules/NSIS.template.in
@@ -29,6 +29,33 @@
 
 ;--------------------------------
 ;General
+!ifdef INNER
+  OutFile "$%TEMP%\tempinstaller.exe"
+  SetCompress off                           ; for speed
+!else
+  ; 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.
+
+  !system "$\"${NSISDIR}\makensis$\" /DINNER $\"@CPACK_TEMPORARY_DIRECTORY@/../project.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.
+
+  !tempfile INCEXIST
+  !system 'if exist "@CPACK_NSIS_SIGN_UNINSTALLER@" echo !define HAVE_SIGN_UNINST > "${INCEXIST}"'
+  !include "${INCEXIST}"
+  !delfile "${INCEXIST}"
+  !ifdef HAVE_SIGN_UNINST
+  !system '"@CPACK_NSIS_SIGN_UNINSTALLER@" "$%TEMP%\Uninstall.exe"' = 0
+  !endif
+
+  ; Good.  Now we can carry on writing the real installer.
 
   ;Name and file
   Name "@CPACK_NSIS_PACKAGE_NAME@"
@@ -36,6 +63,7 @@
 
   ;Set compression
   SetCompressor @CPACK_NSIS_COMPRESSOR@
+!endif
 
   ;Require administrator access
   RequestExecutionLevel admin
@@ -559,8 +587,10 @@ FunctionEnd
   !insertmacro MUI_PAGE_INSTFILES
   !insertmacro MUI_PAGE_FINISH
 
+!ifdef INNER
   !insertmacro MUI_UNPAGE_CONFIRM
   !insertmacro MUI_UNPAGE_INSTFILES
+!endif
 
 ;--------------------------------
 ;Languages
@@ -642,7 +672,10 @@ Section "-Core installation"
   WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
 
   ;Create uninstaller
-  WriteUninstaller "$INSTDIR\Uninstall.exe"
+!ifndef INNER
+  ; this packages the signed uninstaller
+  File "$%TEMP%\Uninstall.exe"
+!endif
   Push "DisplayName"
   Push "@CPACK_NSIS_DISPLAY_NAME@"
   Call ConditionalAddToRegisty
@@ -801,6 +834,7 @@ FunctionEnd
 ;--------------------------------
 ;Uninstaller Section
 
+!ifdef INNER
 Section "Uninstall"
   ReadRegStr $START_MENU SHCTX \
    "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu"
@@ -886,6 +920,7 @@ Section "Uninstall"
     Call un.RemoveFromPath
   doNotRemoveFromPath:
 SectionEnd
+!endif
 
 ;--------------------------------
 ; determine admin versus local install
@@ -898,6 +933,14 @@ 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%\Uninstall.exe"
+  Quit  ; just bail out quickly when running the "inner" installer
+!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"
-- 
GitLab