From a12b0f1b193024b71583b9150aeead33d364d189 Mon Sep 17 00:00:00 2001 From: Nils Gladitz Date: Sun, 7 Feb 2016 20:25:56 +0100 Subject: [PATCH] CMake: Prevent WiX installations over existing NSIS installations Use a custom action to look for Uninstall.exe in the user selected installation prefix. Its presence indicates a previous NSIS installation. Inform the user and request manual resolution of the issue. --- CMakeCPack.cmake | 11 +++++ CMakeCPackOptions.cmake.in | 28 ++++++++++-- Utilities/CMakeLists.txt | 4 ++ Utilities/Release/WiX/CMakeLists.txt | 12 +++++ .../Release/WiX/CustomAction/CMakeLists.txt | 13 ++++++ .../CustomAction/detect_nsis_overwrite.cpp | 45 +++++++++++++++++++ .../Release/WiX/CustomAction/exports.def | 2 + .../WiX/cmake_nsis_overwrite_dialog.wxs | 21 +++++++++ .../Release/WiX/custom_action_dll.wxs.in | 6 +++ Utilities/Release/WiX/install_dir.wxs | 13 +++++- 10 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 Utilities/Release/WiX/CMakeLists.txt create mode 100644 Utilities/Release/WiX/CustomAction/CMakeLists.txt create mode 100644 Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp create mode 100644 Utilities/Release/WiX/CustomAction/exports.def create mode 100644 Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs create mode 100644 Utilities/Release/WiX/custom_action_dll.wxs.in diff --git a/CMakeCPack.cmake b/CMakeCPack.cmake index a0aadcc9f..320327999 100644 --- a/CMakeCPack.cmake +++ b/CMakeCPack.cmake @@ -198,6 +198,17 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") set(CPACK_WIX_UPGRADE_GUID "8ffd1d72-b7f1-11e2-8ee5-00238bca4991") + if(MSVC AND NOT "$ENV{WIX}" STREQUAL "") + set(WIX_CUSTOM_ACTION_ENABLED TRUE) + if(CMAKE_CONFIGURATION_TYPES) + set(WIX_CUSTOM_ACTION_MULTI_CONFIG TRUE) + else() + set(WIX_CUSTOM_ACTION_MULTI_CONFIG FALSE) + endif() + else() + set(WIX_CUSTOM_ACTION_ENABLED FALSE) + endif() + # Set the options file that needs to be included inside CMakeCPackOptions.cmake set(QT_DIALOG_CPACK_OPTIONS_FILE ${CMake_BINARY_DIR}/Source/QtDialog/QtDialogCPack.cmake) configure_file("${CMake_SOURCE_DIR}/CMakeCPackOptions.cmake.in" diff --git a/CMakeCPackOptions.cmake.in b/CMakeCPackOptions.cmake.in index 25af0c93e..59ae224ea 100644 --- a/CMakeCPackOptions.cmake.in +++ b/CMakeCPackOptions.cmake.in @@ -246,6 +246,29 @@ if("${CPACK_GENERATOR}" STREQUAL "WIX") "@CMake_SOURCE_DIR@/Utilities/Release/WiX/cmake_extra_dialog.wxs" ) + set(_WIX_CUSTOM_ACTION_ENABLED "@WIX_CUSTOM_ACTION_ENABLED@") + if(_WIX_CUSTOM_ACTION_ENABLED) + list(APPEND CPACK_WIX_EXTRA_SOURCES + "@CMake_SOURCE_DIR@/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs" + ) + list(APPEND CPACK_WIX_CANDLE_EXTRA_FLAGS -dCHECK_NSIS=1) + + set(_WIX_CUSTOM_ACTION_MULTI_CONFIG "@WIX_CUSTOM_ACTION_MULTI_CONFIG@") + if(_WIX_CUSTOM_ACTION_MULTI_CONFIG) + if(CPACK_BUILD_CONFIG) + set(_WIX_CUSTOM_ACTION_CONFIG "${CPACK_BUILD_CONFIG}") + else() + set(_WIX_CUSTOM_ACTION_CONFIG "Release") + endif() + + list(APPEND CPACK_WIX_EXTRA_SOURCES + "@CMake_BINARY_DIR@/Utilities/Release/WiX/custom_action_dll-${_WIX_CUSTOM_ACTION_CONFIG}.wxs") + else() + list(APPEND CPACK_WIX_EXTRA_SOURCES + "@CMake_BINARY_DIR@/Utilities/Release/WiX/custom_action_dll.wxs") + endif() + endif() + set(CPACK_WIX_UI_REF "CMakeUI_InstallDir") set(CPACK_WIX_PATCH_FILE @@ -261,8 +284,7 @@ if("${CPACK_GENERATOR}" STREQUAL "WIX") if(BUILD_QtDialog) list(APPEND CPACK_WIX_PATCH_FILE "@CMake_SOURCE_DIR@/Utilities/Release/WiX/patch_desktop_shortcut.xml" - ) - - set(CPACK_WIX_CANDLE_EXTRA_FLAGS "-dBUILD_QtDialog=1") + ) + list(APPEND CPACK_WIX_CANDLE_EXTRA_FLAGS -dBUILD_QtDialog=1) endif() endif() diff --git a/Utilities/CMakeLists.txt b/Utilities/CMakeLists.txt index 8b3e325fa..cf6bb7285 100644 --- a/Utilities/CMakeLists.txt +++ b/Utilities/CMakeLists.txt @@ -33,3 +33,7 @@ else() # Normal documentation build. add_subdirectory(Sphinx) endif() + +if(WIX_CUSTOM_ACTION_ENABLED) + add_subdirectory(Release/WiX) +endif() diff --git a/Utilities/Release/WiX/CMakeLists.txt b/Utilities/Release/WiX/CMakeLists.txt new file mode 100644 index 000000000..cc0dbe1a6 --- /dev/null +++ b/Utilities/Release/WiX/CMakeLists.txt @@ -0,0 +1,12 @@ +add_subdirectory(CustomAction) + +if(CMAKE_CONFIGURATION_TYPES) + set(CUSTOM_ACTION_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/custom_action_dll-$.wxs") +else() + set(CUSTOM_ACTION_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/custom_action_dll.wxs") +endif() + +file(GENERATE + OUTPUT "${CUSTOM_ACTION_OUTPUT}" + INPUT "${CMAKE_CURRENT_SOURCE_DIR}/custom_action_dll.wxs.in" + ) diff --git a/Utilities/Release/WiX/CustomAction/CMakeLists.txt b/Utilities/Release/WiX/CustomAction/CMakeLists.txt new file mode 100644 index 000000000..7efd01e00 --- /dev/null +++ b/Utilities/Release/WiX/CustomAction/CMakeLists.txt @@ -0,0 +1,13 @@ +foreach(CONFIG DEBUG MINSIZEREL RELEASE RELWITHDEBINFO) + string(REPLACE "/MD" "/MT" + "CMAKE_CXX_FLAGS_${CONFIG}" + "${CMAKE_CXX_FLAGS_${CONFIG}}" + ) +endforeach() + +add_library(CMakeWiXCustomActions MODULE + detect_nsis_overwrite.cpp + exports.def +) + +target_link_libraries(CMakeWiXCustomActions PRIVATE msi) diff --git a/Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp b/Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp new file mode 100644 index 000000000..dad1ae511 --- /dev/null +++ b/Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp @@ -0,0 +1,45 @@ +#include +#include +#include + +#include +#include + +std::wstring get_property(MSIHANDLE msi_handle, std::wstring const& name) +{ + DWORD size = 0; + + UINT status = MsiGetPropertyW(msi_handle, name.c_str(), L"", &size); + + if(status == ERROR_MORE_DATA) + { + std::vector buffer(size + 1); + MsiGetPropertyW(msi_handle, name.c_str(), &buffer[0], &size); + return std::wstring(&buffer[0]); + } + else + { + return std::wstring(); + } +} + +void set_property(MSIHANDLE msi_handle, + std::wstring const& name, std::wstring const& value) +{ + MsiSetPropertyW(msi_handle, name.c_str(), value.c_str()); +} + +extern "C" UINT __stdcall DetectNsisOverwrite(MSIHANDLE msi_handle) +{ + std::wstring install_root = get_property(msi_handle, L"INSTALL_ROOT"); + + std::wstring uninstall_exe = install_root + L"\\uninstall.exe"; + + bool uninstall_exe_exists = + GetFileAttributesW(uninstall_exe.c_str()) != INVALID_FILE_ATTRIBUTES; + + set_property(msi_handle, L"CMAKE_NSIS_OVERWRITE_DETECTED", + uninstall_exe_exists ? L"1" : L"0"); + + return ERROR_SUCCESS; +} diff --git a/Utilities/Release/WiX/CustomAction/exports.def b/Utilities/Release/WiX/CustomAction/exports.def new file mode 100644 index 000000000..0e448b20e --- /dev/null +++ b/Utilities/Release/WiX/CustomAction/exports.def @@ -0,0 +1,2 @@ +EXPORTS + DetectNsisOverwrite=DetectNsisOverwrite diff --git a/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs b/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs new file mode 100644 index 000000000..8fe60f2cd --- /dev/null +++ b/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs @@ -0,0 +1,21 @@ + + + + + + 1 + + + + Uninstall.exe was detected in your chosen installation prefix. + This indicates a conflicting NSIS based installation of CMake. + + Please uninstall your old CMake installation or choose a different + installation directory. + + + + + + + diff --git a/Utilities/Release/WiX/custom_action_dll.wxs.in b/Utilities/Release/WiX/custom_action_dll.wxs.in new file mode 100644 index 000000000..021e63c5f --- /dev/null +++ b/Utilities/Release/WiX/custom_action_dll.wxs.in @@ -0,0 +1,6 @@ + + + + + diff --git a/Utilities/Release/WiX/install_dir.wxs b/Utilities/Release/WiX/install_dir.wxs index 883efba90..49b74e37d 100644 --- a/Utilities/Release/WiX/install_dir.wxs +++ b/Utilities/Release/WiX/install_dir.wxs @@ -9,6 +9,9 @@ + + + @@ -36,7 +39,11 @@ 1 NOT WIXUI_DONTVALIDATEPATH "1"]]> - WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" + + 1 + CMAKE_NSIS_OVERWRITE_DETECTED="1" + + 1]]> 1 1 @@ -57,5 +64,9 @@ + + + +