diff options
author | Brad King <brad.king@kitware.com> | 2024-03-19 23:41:34 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2024-03-24 19:26:39 (GMT) |
commit | 03884f4f3230f150af26ae1c503d4a43e612323c (patch) | |
tree | 611ee842f6098c2fdffb4c90baff5b454360d23c | |
parent | cfe5bbdc54d59833047689ad416466d7fd9073f1 (diff) | |
download | CMake-03884f4f3230f150af26ae1c503d4a43e612323c.zip CMake-03884f4f3230f150af26ae1c503d4a43e612323c.tar.gz CMake-03884f4f3230f150af26ae1c503d4a43e612323c.tar.bz2 |
CPack/WIX: Add support for WiX Toolset v4
Add a `CPACK_WIX_VERSION` option to specify version WiX for
which the project is configured.
Fixes: #23910
22 files changed, 446 insertions, 82 deletions
diff --git a/.gitlab/ci/configure_windows_wix_common.cmake b/.gitlab/ci/configure_windows_wix_common.cmake index faf2464..4219c41 100644 --- a/.gitlab/ci/configure_windows_wix_common.cmake +++ b/.gitlab/ci/configure_windows_wix_common.cmake @@ -1,2 +1,5 @@ get_filename_component(wix3_dir "${CMAKE_CURRENT_LIST_DIR}/../wix3" ABSOLUTE) set(CMake_TEST_CPACK_WIX3 "${wix3_dir}" CACHE PATH "") + +get_filename_component(wix4_dir "${CMAKE_CURRENT_LIST_DIR}/../wix4" ABSOLUTE) +set(CMake_TEST_CPACK_WIX4 "${wix4_dir}" CACHE PATH "") diff --git a/.gitlab/ci/env_windows_arm64_vs2022_ninja.ps1 b/.gitlab/ci/env_windows_arm64_vs2022_ninja.ps1 index b0085d2..eb7bf6e 100755 --- a/.gitlab/ci/env_windows_arm64_vs2022_ninja.ps1 +++ b/.gitlab/ci/env_windows_arm64_vs2022_ninja.ps1 @@ -1 +1,2 @@ & "$pwsh" -File .gitlab/ci/wix3.ps1 +& "$pwsh" -File .gitlab/ci/wix4.ps1 diff --git a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 index 9dde3a2..ae4a058 100755 --- a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 +++ b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 @@ -5,3 +5,4 @@ if ("$env:CMAKE_CI_NIGHTLY" -eq "true") { } & "$pwsh" -File .gitlab/ci/wix3.ps1 +& "$pwsh" -File .gitlab/ci/wix4.ps1 diff --git a/Help/cpack_gen/wix.rst b/Help/cpack_gen/wix.rst index 4b627e7..dfa3434 100644 --- a/Help/cpack_gen/wix.rst +++ b/Help/cpack_gen/wix.rst @@ -9,8 +9,68 @@ Use the `WiX Toolset`_ to produce a Windows Installer ``.msi`` database. The :variable:`CPACK_COMPONENT_<compName>_DISABLED` variable is now supported. +WiX Toolsets +^^^^^^^^^^^^ + +CPack selects one of the following variants of the WiX Toolset +based on the :variable:`CPACK_WIX_VERSION` variable: + +* `WiX .NET Tools`_ +* `WiX Toolset v3`_ + +WiX .NET Tools +"""""""""""""" + +Packaging is performed using the following tools: + +``wix build`` + Build WiX source files directly into a Windows Installer ``.msi`` database. + + Invocations may be customized using tool-specific variables: + + * :variable:`CPACK_WIX_BUILD_EXTENSIONS <CPACK_WIX_<TOOL>_EXTENSIONS>` + * :variable:`CPACK_WIX_BUILD_EXTRA_FLAGS <CPACK_WIX_<TOOL>_EXTRA_FLAGS>` + +WiX extensions must be named with the form ``WixToolset.<Name>.wixext``. + +CPack expects the ``wix`` .NET tool to be available for command-line use +with any required WiX extensions already installed. Be sure the ``wix`` +version is compatible with :variable:`CPACK_WIX_VERSION`, and that WiX +extension versions match the ``wix`` tool version. For example: + +1. Install the ``wix`` command-line tool using ``dotnet``. + + To install ``wix`` globally for the current user: + + .. code-block:: bat + + dotnet tool install --global wix --version 4.0.4 + + This places ``wix.exe`` in ``%USERPROFILE%\.dotnet\tools`` and adds + the directory to the current user's ``PATH`` environment variable. + + Or, to install ``wix`` in a specific path, e.g., in ``c:\WiX``: + + .. code-block:: bat + + dotnet tool install --tool-path c:\WiX wix --version 4.0.4 + + This places ``wix.exe`` in ``c:\WiX``, but does *not* add it to the + current user's ``PATH`` environment variable. The ``WIX`` environment + variable may be set to tell CPack where to find the tool, + e.g., ``set WIX=c:\WiX``. + +2. Add the WiX ``UI`` extension, needed by CPack's default WiX template: + + .. code-block:: bat + + wix extension add --global WixToolset.UI.wixext/4.0.4 + + Extensions added globally are stored in ``%USERPROFILE%\.wix``, or if the + ``WIX_EXTENSIONS`` environment variable is set, in ``%WIX_EXTENSIONS%\.wix``. + WiX Toolset v3 -^^^^^^^^^^^^^^ +"""""""""""""" Packaging is performed using the following tools: @@ -45,6 +105,19 @@ Variables specific to CPack WIX generator The following variables are specific to the installers built on Windows using WiX. +.. variable:: CPACK_WIX_VERSION + + .. versionadded:: 3.30 + + Specify the version of WiX Toolset for which the configuration + is written. The value must be one of + + ``4`` + Package using `WiX .NET Tools`_. + + ``3`` + Package using `WiX Toolset v3`_. This is the default. + .. variable:: CPACK_WIX_UPGRADE_GUID Upgrade GUID (``Product/@UpgradeCode``) @@ -101,8 +174,13 @@ Windows using WiX. .. variable:: CPACK_WIX_UI_REF - Specify the WiX ``UI`` extension's dialog set. - This is the Id of the ``<UIRef>`` element in the default WiX template. + Specify the WiX ``UI`` extension's dialog set: + + * With `WiX .NET Tools`_, this is the Id of the + ``<ui:WixUI>`` element in the default WiX template. + + * With `WiX Toolset v3`_, this is the Id of the + ``<UIRef>`` element in the default WiX template. The default is ``WixUI_InstallDir`` in case no CPack components have been defined and ``WixUI_FeatureTree`` otherwise. @@ -234,7 +312,7 @@ Windows using WiX. .. variable:: CPACK_WIX_EXTRA_OBJECTS - Extra WiX object files or libraries. + Extra WiX object files or libraries to use with `WiX Toolset v3`_. This variable provides an optional list of extra WiX object (``.wixobj``) and/or WiX library (``.wixlib``) files. The paths must be absolute. @@ -242,17 +320,17 @@ Windows using WiX. .. variable:: CPACK_WIX_EXTENSIONS Specify a list of additional extensions for WiX tools. - See `WiX Toolset v3`_ for extension naming patterns. + See `WiX Toolsets`_ for extension naming patterns. .. variable:: CPACK_WIX_<TOOL>_EXTENSIONS Specify a list of additional extensions for a specific WiX tool. - See `WiX Toolset v3`_ for possible ``<TOOL>`` names. + See `WiX Toolsets`_ for possible ``<TOOL>`` names. .. variable:: CPACK_WIX_<TOOL>_EXTRA_FLAGS Specify a list of additional command-line flags for a specific WiX tool. - See `WiX Toolset v3`_ for possible ``<TOOL>`` names. + See `WiX Toolsets`_ for possible ``<TOOL>`` names. Use it at your own risk. Future versions of CPack may generate flags which may be in conflict @@ -356,8 +434,8 @@ Windows using WiX. .. versionadded:: 3.23 If this variable is set to true, the default inclusion of the WiX ``UI`` - extension is skipped, i.e., the ``-ext WixUIExtension`` flag is not - passed to WiX tools. + extension is skipped, i.e., the ``-ext WixUIExtension`` or + ``-ext WixToolset.UI.wixext`` flag is not passed to WiX tools. .. variable:: CPACK_WIX_ARCHITECTURE @@ -386,6 +464,9 @@ Windows using WiX. ``NONE`` Create an installer without any ``InstallScope`` attribute. + This is not supported if :variable:`CPACK_WIX_VERSION` is set + to any value other than ``3``. + .. deprecated:: 3.29 This value is only for compatibility with the inconsistent behavior used diff --git a/Help/release/dev/cpack-wix.rst b/Help/release/dev/cpack-wix.rst new file mode 100644 index 0000000..020dfeb --- /dev/null +++ b/Help/release/dev/cpack-wix.rst @@ -0,0 +1,5 @@ +cpack-wix +--------- + +* The :cpack_gen:`CPack WIX Generator` gained support for WiX Toolset v4. + See the :variable:`CPACK_WIX_VERSION` variable. diff --git a/Modules/Internal/CPack/CPackWIX.cmake b/Modules/Internal/CPack/CPackWIX.cmake index 5fe772e..103d21c 100644 --- a/Modules/Internal/CPack/CPackWIX.cmake +++ b/Modules/Internal/CPack/CPackWIX.cmake @@ -5,18 +5,24 @@ if(NOT CPACK_WIX_ROOT) string(REPLACE "\\" "/" CPACK_WIX_ROOT "$ENV{WIX}") endif() -find_program(CPACK_WIX_CANDLE_EXECUTABLE candle - PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") +if(CPACK_WIX_VERSION VERSION_GREATER_EQUAL 4) + find_program(CPACK_WIX_EXECUTABLE NAMES wix + PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") + if(NOT CPACK_WIX_EXECUTABLE) + message(FATAL_ERROR "Could not find the 'wix' executable.") + endif() +else() + find_program(CPACK_WIX_CANDLE_EXECUTABLE candle + PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") + if(NOT CPACK_WIX_CANDLE_EXECUTABLE) + message(FATAL_ERROR "Could not find the WiX candle executable.") + endif() -if(NOT CPACK_WIX_CANDLE_EXECUTABLE) - message(FATAL_ERROR "Could not find the WiX candle executable.") -endif() - -find_program(CPACK_WIX_LIGHT_EXECUTABLE light - PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") - -if(NOT CPACK_WIX_LIGHT_EXECUTABLE) - message(FATAL_ERROR "Could not find the WiX light executable.") + find_program(CPACK_WIX_LIGHT_EXECUTABLE light + PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") + if(NOT CPACK_WIX_LIGHT_EXECUTABLE) + message(FATAL_ERROR "Could not find the WiX light executable.") + endif() endif() if(NOT DEFINED CPACK_WIX_INSTALL_SCOPE) diff --git a/Modules/Internal/CPack/WIX.template.in b/Modules/Internal/CPack/WIX.template.in new file mode 100644 index 0000000..7cad186 --- /dev/null +++ b/Modules/Internal/CPack/WIX.template.in @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?include "cpack_variables.wxi"?> + +<Wix + xmlns="http://wixtoolset.org/schemas/v4/wxs"@CPACK_WIX_CUSTOM_XMLNS_EXPANDED@ + RequiredVersion="4.0" + > + + <Package + Name="$(var.CPACK_PACKAGE_NAME)" + Version="$(var.CPACK_PACKAGE_VERSION)" + Manufacturer="$(var.CPACK_PACKAGE_VENDOR)" + UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)" + ProductCode="$(var.CPACK_WIX_PRODUCT_GUID)" + Scope="$(var.CPACK_WIX_INSTALL_SCOPE)" + InstallerVersion="500" + Language="1033" + Compressed="yes" + > + + <Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/> + + <MajorUpgrade + Schedule="afterInstallInitialize" + AllowSameVersionUpgrades="yes" + DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."/> + + <WixVariable Id="WixUILicenseRtf" Value="$(var.CPACK_WIX_LICENSE_RTF)"/> + <Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/> + + <?ifdef CPACK_WIX_PRODUCT_ICON?> + <Property Id="ARPPRODUCTICON" Value="ProductIcon.ico" /> + <Icon Id="ProductIcon.ico" SourceFile="$(var.CPACK_WIX_PRODUCT_ICON)"/> + <?endif?> + + <?ifdef CPACK_WIX_UI_BANNER?> + <WixVariable Id="WixUIBannerBmp" Value="$(var.CPACK_WIX_UI_BANNER)"/> + <?endif?> + + <?ifdef CPACK_WIX_UI_DIALOG?> + <WixVariable Id="WixUIDialogBmp" Value="$(var.CPACK_WIX_UI_DIALOG)"/> + <?endif?> + + <FeatureRef Id="ProductFeature"/> + + <ui:WixUI Id="$(var.CPACK_WIX_UI_REF)" /> + <UIRef Id="WixUI_ErrorProgressText" /> + + <?include "properties.wxi"?> + <?include "product_fragment.wxi"?> + </Package> +</Wix> diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index b1bb0ca..8532b3e 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -166,6 +166,16 @@ int cmCPackWIXGenerator::PackageFiles() bool cmCPackWIXGenerator::InitializeWiXConfiguration() { + if (cmValue wixVersion = GetOption("CPACK_WIX_VERSION")) { + if (!cmStrToULong(*wixVersion, &this->WixVersion) || + this->WixVersion < 3 || this->WixVersion > 4) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_WIX_VERSION has unknown value '" + << *wixVersion << "'" << std::endl); + return false; + } + } + if (!ReadListFile("Internal/CPack/CPackWIX.cmake")) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while executing CPackWIX.cmake" << std::endl); @@ -232,14 +242,22 @@ bool cmCPackWIXGenerator::InitializeWiXConfiguration() SetOption("CPACK_WIX_PROPERTY_ARPCONTACT", packageContact); } - CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions); - CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions); + if (this->WixVersion >= 4) { + CollectExtensions("CPACK_WIX_EXTENSIONS", this->WixExtensions); + if (!GetOption("CPACK_WIX_SKIP_WIX_UI_EXTENSION").IsOn()) { + this->WixExtensions.insert("WixToolset.UI.wixext"); + } + } else { + CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions); + CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions); - if (!GetOption("CPACK_WIX_SKIP_WIX_UI_EXTENSION").IsOn()) { - this->LightExtensions.insert("WixUIExtension"); + if (!GetOption("CPACK_WIX_SKIP_WIX_UI_EXTENSION").IsOn()) { + this->LightExtensions.insert("WixUIExtension"); + } + CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions); + CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions); } - CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions); - CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions); + CollectXmlNamespaces("CPACK_WIX_CUSTOM_XMLNS", this->CustomXmlNamespaces); cmValue patchFilePath = GetOption("CPACK_WIX_PATCH_FILE"); @@ -278,6 +296,53 @@ bool cmCPackWIXGenerator::PackageFilesImpl() AppendUserSuppliedExtraSources(); + return this->WixVersion >= 4 ? this->PackageWithWix() + : this->PackageWithWix3(); +} + +bool cmCPackWIXGenerator::PackageWithWix() +{ + std::string wixExecutable; + if (!RequireOption("CPACK_WIX_EXECUTABLE", wixExecutable)) { + return false; + } + + std::string arch; + if (cmValue archOpt = GetOption("CPACK_WIX_ARCHITECTURE")) { + arch = *archOpt; + } else { + arch = GetArchitecture(); + cmCPackLogger( + cmCPackLog::LOG_VERBOSE, + "CPACK_WIX_ARCHITECTURE was not set. Invoking WiX with architecture " + << arch << ". " << std::endl); + } + + std::ostringstream command; + command << QuotePath(wixExecutable) << " build" + << " -arch " << arch << " -out " + << QuotePath(CMakeToWixPath(packageFileNames.at(0))); + + for (std::string const& ext : this->WixExtensions) { + command << " -ext " << QuotePath(ext); + } + + cmList cultures{ GetOption("CPACK_WIX_CULTURES") }; + for (std::string const& culture : cultures) { + command << " -culture \"" << culture << "\""; + } + + AddCustomFlags("CPACK_WIX_BUILD_EXTRA_FLAGS", command); + + for (std::string const& sourceFilename : this->WixSources) { + command << " -src " << QuotePath(CMakeToWixPath(sourceFilename)); + } + + return RunWiXCommand(command.str()); +} + +bool cmCPackWIXGenerator::PackageWithWix3() +{ std::set<std::string> usedBaseNames; std::ostringstream objectFiles; @@ -341,8 +406,8 @@ void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile() std::string includeFilename = cmStrCat(this->CPackTopLevel, "/cpack_variables.wxi"); - cmWIXSourceWriter includeFile(this->Logger, includeFilename, - this->ComponentGuidType, + cmWIXSourceWriter includeFile(this->WixVersion, this->Logger, + includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); InjectXmlNamespaces(includeFile); @@ -367,8 +432,8 @@ void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile() std::string includeFilename = cmStrCat(this->CPackTopLevel, "/properties.wxi"); - cmWIXSourceWriter includeFile(this->Logger, includeFilename, - this->ComponentGuidType, + cmWIXSourceWriter includeFile(this->WixVersion, this->Logger, + includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); InjectXmlNamespaces(includeFile); @@ -417,8 +482,8 @@ void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile() std::string includeFilename = cmStrCat(this->CPackTopLevel, "/product_fragment.wxi"); - cmWIXSourceWriter includeFile(this->Logger, includeFilename, - this->ComponentGuidType, + cmWIXSourceWriter includeFile(this->WixVersion, this->Logger, + includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); InjectXmlNamespaces(includeFile); @@ -459,7 +524,8 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() this->WixSources.push_back(directoryDefinitionsFilename); cmWIXDirectoriesSourceWriter directoryDefinitions( - this->Logger, directoryDefinitionsFilename, this->ComponentGuidType); + this->WixVersion, this->Logger, directoryDefinitionsFilename, + this->ComponentGuidType); InjectXmlNamespaces(directoryDefinitions); directoryDefinitions.BeginElement("Fragment"); @@ -468,11 +534,13 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() return false; } - directoryDefinitions.BeginElement("Directory"); - directoryDefinitions.AddAttribute("Id", "TARGETDIR"); - directoryDefinitions.AddAttribute("Name", "SourceDir"); + if (this->WixVersion == 3) { + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", "TARGETDIR"); + directoryDefinitions.AddAttribute("Name", "SourceDir"); + } - size_t installRootSize = + auto installationPrefixDirectory = directoryDefinitions.BeginInstallationPrefixDirectory(GetRootFolderId(), installRoot); @@ -481,7 +549,8 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() this->WixSources.push_back(fileDefinitionsFilename); - cmWIXFilesSourceWriter fileDefinitions(this->Logger, fileDefinitionsFilename, + cmWIXFilesSourceWriter fileDefinitions(this->WixVersion, this->Logger, + fileDefinitionsFilename, this->ComponentGuidType); InjectXmlNamespaces(fileDefinitions); @@ -492,8 +561,9 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() this->WixSources.push_back(featureDefinitionsFilename); - cmWIXFeaturesSourceWriter featureDefinitions( - this->Logger, featureDefinitionsFilename, this->ComponentGuidType); + cmWIXFeaturesSourceWriter featureDefinitions(this->WixVersion, this->Logger, + featureDefinitionsFilename, + this->ComponentGuidType); InjectXmlNamespaces(featureDefinitions); featureDefinitions.BeginElement("Fragment"); @@ -501,7 +571,11 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() featureDefinitions.BeginElement("Feature"); featureDefinitions.AddAttribute("Id", "ProductFeature"); featureDefinitions.AddAttribute("Display", "expand"); - featureDefinitions.AddAttribute("Absent", "disallow"); + if (this->WixVersion >= 4) { + featureDefinitions.AddAttribute("AllowAbsent", "no"); + } else { + featureDefinitions.AddAttribute("Absent", "disallow"); + } featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT"); std::string cpackPackageName; @@ -583,7 +657,8 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() featureDefinitions.EndElement("Fragment"); fileDefinitions.EndElement("Fragment"); - directoryDefinitions.EndInstallationPrefixDirectory(installRootSize); + directoryDefinitions.EndInstallationPrefixDirectory( + installationPrefixDirectory); if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) != emittedShortcutTypes.end()) { @@ -601,7 +676,9 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() directoryDefinitions.EmitStartupFolder(); } - directoryDefinitions.EndElement("Directory"); + if (this->WixVersion == 3) { + directoryDefinitions.EndElement("Directory"); + } directoryDefinitions.EndElement("Fragment"); if (!GenerateMainSourceFileFromTemplate()) { @@ -613,15 +690,19 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() std::string cmCPackWIXGenerator::GetRootFolderId() const { + std::string result; + if (GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER").IsOn()) { - return ""; + return result; } - std::string result = "ProgramFiles<64>Folder"; - cmValue rootFolderId = GetOption("CPACK_WIX_ROOT_FOLDER_ID"); if (rootFolderId) { result = *rootFolderId; + } else if (this->WixVersion >= 4) { + result = "ProgramFiles6432Folder"; + } else { + result = "ProgramFiles<64>Folder"; } if (GetArchitecture() == "x86"_s) { @@ -640,7 +721,9 @@ bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate() wixTemplate = *wixtpl; } else { cm::optional<cm::string_view> alt; - alt = "WIX-v3/"_s; + if (this->WixVersion == 3) { + alt = "WIX-v3/"_s; + } wixTemplate = FindTemplate("WIX.template.in"_s, alt); } @@ -768,21 +851,31 @@ bool cmCPackWIXGenerator::CreateShortcutsOfSpecificType( cmWIXFeaturesSourceWriter& featureDefinitions) { std::string directoryId; + std::string directoryRef = "DirectoryRef"; switch (type) { case cmWIXShortcuts::START_MENU: { cmValue cpackWixProgramMenuFolder = GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER"); if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == "."_s) { directoryId = "ProgramMenuFolder"; + if (this->WixVersion >= 4) { + directoryRef = "StandardDirectory"; + } } else { directoryId = "PROGRAM_MENU_FOLDER"; } } break; case cmWIXShortcuts::DESKTOP: directoryId = "DesktopFolder"; + if (this->WixVersion >= 4) { + directoryRef = "StandardDirectory"; + } break; case cmWIXShortcuts::STARTUP: directoryId = "StartupFolder"; + if (this->WixVersion >= 4) { + directoryRef = "StandardDirectory"; + } break; default: return false; @@ -814,7 +907,7 @@ bool cmCPackWIXGenerator::CreateShortcutsOfSpecificType( componentId += idSuffix; - fileDefinitions.BeginElement("DirectoryRef"); + fileDefinitions.BeginElement(directoryRef); fileDefinitions.AddAttribute("Id", directoryId); fileDefinitions.BeginElement("Component"); @@ -844,7 +937,7 @@ bool cmCPackWIXGenerator::CreateShortcutsOfSpecificType( } fileDefinitions.EndElement("Component"); - fileDefinitions.EndElement("DirectoryRef"); + fileDefinitions.EndElement(directoryRef); featureDefinitions.EmitComponentRef(componentId); featureDefinitions.EndElement("FeatureRef"); @@ -1199,6 +1292,12 @@ void cmCPackWIXGenerator::CollectXmlNamespaces(std::string const& variableName, } } std::string xmlns; + if (this->WixVersion >= 4 && + cm::contains(this->WixExtensions, "WixToolset.UI.wixext") && + !cm::contains(namespaces, "ui")) { + xmlns = cmStrCat( + xmlns, "\n xmlns:ui=\"http://wixtoolset.org/schemas/v4/wxs/ui\""); + } for (auto& ns : namespaces) { xmlns = cmStrCat(xmlns, "\n xmlns:", ns.first, "=\"", cmWIXSourceWriter::EscapeAttributeValue(ns.second), '"'); diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h index 8609cf3..63530d4 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.h +++ b/Source/CPack/WiX/cmCPackWIXGenerator.h @@ -59,6 +59,8 @@ private: bool InitializeWiXConfiguration(); bool PackageFilesImpl(); + bool PackageWithWix(); + bool PackageWithWix3(); void CreateWiXVariablesIncludeFile(); @@ -160,6 +162,7 @@ private: id_map_t PathToIdMap; ambiguity_map_t IdAmbiguityCounter; + extension_set_t WixExtensions; extension_set_t CandleExtensions; extension_set_t LightExtensions; xmlns_map_t CustomXmlNamespaces; @@ -168,5 +171,7 @@ private: std::unique_ptr<cmWIXPatch> Patch; + unsigned long WixVersion = 3; + cmWIXSourceWriter::GuidType ComponentGuidType; }; diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx index a655d86..b1e7bd2 100644 --- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx @@ -5,15 +5,16 @@ #include <cmext/string_view> cmWIXDirectoriesSourceWriter::cmWIXDirectoriesSourceWriter( - cmCPackLog* logger, std::string const& filename, GuidType componentGuidType) - : cmWIXSourceWriter(logger, filename, componentGuidType) + unsigned long wixVersion, cmCPackLog* logger, std::string const& filename, + GuidType componentGuidType) + : cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType) { } void cmWIXDirectoriesSourceWriter::EmitStartMenuFolder( std::string const& startMenuFolder) { - BeginElement("Directory"); + BeginElement_StandardDirectory(); AddAttribute("Id", "ProgramMenuFolder"); if (startMenuFolder != "."_s) { @@ -23,34 +24,39 @@ void cmWIXDirectoriesSourceWriter::EmitStartMenuFolder( EndElement("Directory"); } - EndElement("Directory"); + EndElement_StandardDirectory(); } void cmWIXDirectoriesSourceWriter::EmitDesktopFolder() { - BeginElement("Directory"); + BeginElement_StandardDirectory(); AddAttribute("Id", "DesktopFolder"); - AddAttribute("Name", "Desktop"); - EndElement("Directory"); + if (this->WixVersion == 3) { + AddAttribute("Name", "Desktop"); + } + EndElement_StandardDirectory(); } void cmWIXDirectoriesSourceWriter::EmitStartupFolder() { - BeginElement("Directory"); + BeginElement_StandardDirectory(); AddAttribute("Id", "StartupFolder"); - AddAttribute("Name", "Startup"); - EndElement("Directory"); + if (this->WixVersion == 3) { + AddAttribute("Name", "Startup"); + } + EndElement_StandardDirectory(); } -size_t cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory( +cmWIXDirectoriesSourceWriter::InstallationPrefixDirectory +cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory( std::string const& programFilesFolderId, std::string const& installRootString) { - size_t offset = 1; + InstallationPrefixDirectory installationPrefixDirectory; if (!programFilesFolderId.empty()) { - BeginElement("Directory"); + installationPrefixDirectory.HasStandardDirectory = true; + this->BeginElement_StandardDirectory(); AddAttribute("Id", programFilesFolderId); - offset = 0; } std::vector<std::string> installRoot; @@ -62,6 +68,7 @@ size_t cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory( } for (size_t i = 1; i < installRoot.size(); ++i) { + ++installationPrefixDirectory.Depth; BeginElement("Directory"); if (i == installRoot.size() - 1) { @@ -75,12 +82,16 @@ size_t cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory( AddAttribute("Name", installRoot[i]); } - return installRoot.size() - offset; + return installationPrefixDirectory; } -void cmWIXDirectoriesSourceWriter::EndInstallationPrefixDirectory(size_t size) +void cmWIXDirectoriesSourceWriter::EndInstallationPrefixDirectory( + InstallationPrefixDirectory installationPrefixDirectory) { - for (size_t i = 0; i < size; ++i) { + for (size_t i = 0; i < installationPrefixDirectory.Depth; ++i) { EndElement("Directory"); } + if (installationPrefixDirectory.HasStandardDirectory) { + this->EndElement_StandardDirectory(); + } } diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h index 0af3094..b0aa1e2 100644 --- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h @@ -13,7 +13,8 @@ class cmWIXDirectoriesSourceWriter : public cmWIXSourceWriter { public: - cmWIXDirectoriesSourceWriter(cmCPackLog* logger, std::string const& filename, + cmWIXDirectoriesSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType); void EmitStartMenuFolder(std::string const& startMenuFolder); @@ -22,9 +23,16 @@ public: void EmitStartupFolder(); - size_t BeginInstallationPrefixDirectory( + struct InstallationPrefixDirectory + { + bool HasStandardDirectory = false; + size_t Depth = 0; + }; + + InstallationPrefixDirectory BeginInstallationPrefixDirectory( std::string const& programFilesFolderId, std::string const& installRootString); - void EndInstallationPrefixDirectory(size_t size); + void EndInstallationPrefixDirectory( + InstallationPrefixDirectory installationPrefixDirectory); }; diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx index 78c2208..0d2e6e8 100644 --- a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx @@ -5,8 +5,9 @@ #include "cmStringAlgorithms.h" cmWIXFeaturesSourceWriter::cmWIXFeaturesSourceWriter( - cmCPackLog* logger, std::string const& filename, GuidType componentGuidType) - : cmWIXSourceWriter(logger, filename, componentGuidType) + unsigned long wixVersion, cmCPackLog* logger, std::string const& filename, + GuidType componentGuidType) + : cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType) { } @@ -69,7 +70,11 @@ void cmWIXFeaturesSourceWriter::EmitFeatureForComponent( AddAttributeUnlessEmpty("Description", component.Description); if (component.IsRequired) { - AddAttribute("Absent", "disallow"); + if (this->WixVersion >= 4) { + AddAttribute("AllowAbsent", "no"); + } else { + AddAttribute("Absent", "disallow"); + } } if (component.IsHidden) { diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h index 0facf97..95de8fe 100644 --- a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h @@ -12,7 +12,8 @@ class cmWIXFeaturesSourceWriter : public cmWIXSourceWriter { public: - cmWIXFeaturesSourceWriter(cmCPackLog* logger, std::string const& filename, + cmWIXFeaturesSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType); void CreateCMakePackageRegistryEntry(std::string const& package, diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx index b4085d5..87ac6ac 100644 --- a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx @@ -15,10 +15,11 @@ #include "cmUuid.h" #include "cmWIXAccessControlList.h" -cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(cmCPackLog* logger, +cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(unsigned long wixVersion, + cmCPackLog* logger, std::string const& filename, GuidType componentGuidType) - : cmWIXSourceWriter(logger, filename, componentGuidType) + : cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType) { } diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.h b/Source/CPack/WiX/cmWIXFilesSourceWriter.h index 60dddd4..f560304 100644 --- a/Source/CPack/WiX/cmWIXFilesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.h @@ -13,7 +13,8 @@ class cmWIXFilesSourceWriter : public cmWIXSourceWriter { public: - cmWIXFilesSourceWriter(cmCPackLog* logger, std::string const& filename, + cmWIXFilesSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType); void EmitShortcut(std::string const& id, cmWIXShortcut const& shortcut, diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx index ef6712a..33e8a70 100644 --- a/Source/CPack/WiX/cmWIXSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx @@ -8,11 +8,13 @@ #include "cmCryptoHash.h" #include "cmUuid.h" -cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger, +cmWIXSourceWriter::cmWIXSourceWriter(unsigned long wixVersion, + cmCPackLog* logger, std::string const& filename, GuidType componentGuidType, RootElementType rootElementType) - : Logger(logger) + : WixVersion(wixVersion) + , Logger(logger) , File(filename.c_str()) , State(DEFAULT) , SourceFilename(filename) @@ -26,7 +28,11 @@ cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger, BeginElement("Wix"); } - AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi"); + if (this->WixVersion >= 4) { + AddAttribute("xmlns", "http://wixtoolset.org/schemas/v4/wxs"); + } else { + AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi"); + } } cmWIXSourceWriter::~cmWIXSourceWriter() @@ -42,6 +48,24 @@ cmWIXSourceWriter::~cmWIXSourceWriter() EndElement(Elements.back()); } +void cmWIXSourceWriter::BeginElement_StandardDirectory() +{ + if (this->WixVersion >= 4) { + BeginElement("StandardDirectory"); + } else { + BeginElement("Directory"); + } +} + +void cmWIXSourceWriter::EndElement_StandardDirectory() +{ + if (this->WixVersion >= 4) { + EndElement("StandardDirectory"); + } else { + EndElement("Directory"); + } +} + void cmWIXSourceWriter::BeginElement(std::string const& name) { if (State == BEGIN) { diff --git a/Source/CPack/WiX/cmWIXSourceWriter.h b/Source/CPack/WiX/cmWIXSourceWriter.h index f643acd..1089cf5 100644 --- a/Source/CPack/WiX/cmWIXSourceWriter.h +++ b/Source/CPack/WiX/cmWIXSourceWriter.h @@ -27,12 +27,15 @@ public: INCLUDE_ELEMENT_ROOT }; - cmWIXSourceWriter(cmCPackLog* logger, std::string const& filename, - GuidType componentGuidType, + cmWIXSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType, RootElementType rootElementType = WIX_ELEMENT_ROOT); ~cmWIXSourceWriter(); + void BeginElement_StandardDirectory(); + void EndElement_StandardDirectory(); + void BeginElement(std::string const& name); void EndElement(std::string const& name); @@ -52,6 +55,7 @@ public: static std::string EscapeAttributeValue(std::string const& value); protected: + unsigned long WixVersion; cmCPackLog* Logger; private: diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index ec05b90..575a321 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -1100,9 +1100,10 @@ endif() add_RunCMake_test_group(CPack "${cpack_tests}") -if(CMake_TEST_CPACK_WIX3) +if(CMake_TEST_CPACK_WIX3 OR CMake_TEST_CPACK_WIX4) add_RunCMake_test(CPack_WIX -DCMake_TEST_CPACK_WIX3=${CMake_TEST_CPACK_WIX3} + -DCMake_TEST_CPACK_WIX4=${CMake_TEST_CPACK_WIX4} ) endif() diff --git a/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-check.cmake b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-check.cmake new file mode 100644 index 0000000..a7a28ae --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-check.cmake @@ -0,0 +1 @@ +include(${RunCMake_SOURCE_DIR}/cpack-check-common.cmake) diff --git a/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-stdout.txt b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-stdout.txt new file mode 100644 index 0000000..51f06ca --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-stdout.txt @@ -0,0 +1,11 @@ +CPack: Create package using WIX +CPack: Install projects +CPack: - Install project: CPackWiXGenerator \[Release\] +CPack: - Install component: applications +CPack: - Install component: applications2 +CPack: - Install component: extras +CPack: - Install component: headers +CPack: - Install component: libraries +CPack: Create package +CPack: - package: [^ +]*/Tests/RunCMake/CPack_WIX/4-AppWiX-build/MyLib-1\.0\.0-(win64|windows-arm64)\.msi generated\. diff --git a/Tests/RunCMake/CPack_WIX/4-AppWiX-verify-stdout.txt b/Tests/RunCMake/CPack_WIX/4-AppWiX-verify-stdout.txt new file mode 100644 index 0000000..a379859 --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/4-AppWiX-verify-stdout.txt @@ -0,0 +1,34 @@ +-- MyLib-1\.0\.0-(win64|windows-arm64)\.msi +Component: 'CM_CP_applications.bin.my_libapp.exe' 'CM_DP_applications.bin' +Component: 'CM_SHORTCUT_applications' 'PROGRAM_MENU_FOLDER' +Component: 'CM_SHORTCUT_DESKTOP_applications' 'DesktopFolder' +Component: 'CM_CP_applications2.bin.my_other_app.exe' 'CM_DP_applications2.bin' +Component: 'CM_SHORTCUT_applications2' 'PROGRAM_MENU_FOLDER' +Component: 'CM_SHORTCUT_DESKTOP_applications2' 'DesktopFolder' +Component: 'CM_C_EMPTY_CM_DP_extras.extras.empty' 'CM_DP_extras.extras.empty' +Component: 'CM_CP_headers.include.file_with_spaces.h' 'CM_DP_headers.include' +Component: 'CM_CP_headers.include.mylib.h' 'CM_DP_headers.include' +Component: 'CM_CP_libraries.lib.mylib.lib' 'CM_DP_libraries.lib' +Directory: 'INSTALL_ROOT' 'ProgramFiles6432Folder' '[^']*\|CPack Component Example' +Directory: 'CM_DP_applications.bin' 'INSTALL_ROOT' 'bin' +Directory: 'CM_DP_applications2.bin' 'INSTALL_ROOT' 'bin' +Directory: 'CM_DP_extras.extras.empty' 'CM_DP_extras.extras' 'empty' +Directory: 'CM_DP_extras.extras' 'INSTALL_ROOT' 'extras' +Directory: 'CM_DP_headers.include' 'INSTALL_ROOT' 'include' +Directory: 'CM_DP_libraries.lib' 'INSTALL_ROOT' 'lib' +Directory: 'ProgramFiles6432Folder' 'ProgramFiles64Folder' '.' +Directory: 'PROGRAM_MENU_FOLDER' 'ProgramMenuFolder' 'MyLib' +Directory: 'ProgramMenuFolder' 'TARGETDIR' 'PMenu' +Directory: 'ProgramFiles64Folder' 'TARGETDIR' 'PFiles64' +Directory: 'TARGETDIR' '' 'SourceDir' +Directory: 'DesktopFolder' 'TARGETDIR' 'Desktop' +File: 'CM_FP_applications.bin.my_libapp.exe' 'CM_CP_applications.bin.my_libapp.exe' '[^']*\|my-libapp.exe' +File: 'CM_FP_applications2.bin.my_other_app.exe' 'CM_CP_applications2.bin.my_other_app.exe' '[^']*\|my-other-app.exe' +File: 'CM_FP_headers.include.file_with_spaces.h' 'CM_CP_headers.include.file_with_spaces.h' '[^']*\|file with spaces.h' +File: 'CM_FP_headers.include.mylib.h' 'CM_CP_headers.include.mylib.h' 'mylib.h' +File: 'CM_FP_libraries.lib.mylib.lib' 'CM_CP_libraries.lib.mylib.lib' 'mylib.lib' +Shortcut: 'CM_SP_applications.bin.my_libapp.exe' 'PROGRAM_MENU_FOLDER' '[^']*\|CPack WiX Test' 'CM_SHORTCUT_applications' +Shortcut: 'CM_DSP_applications.bin.my_libapp.exe' 'DesktopFolder' '[^']*\|CPack WiX Test' 'CM_SHORTCUT_DESKTOP_applications' +Shortcut: 'CM_SP_applications2.bin.my_other_app.exe' 'PROGRAM_MENU_FOLDER' '[^']*\|Second CPack WiX Test' 'CM_SHORTCUT_applications2' +Shortcut: 'CM_DSP_applications2.bin.my_other_app.exe' 'DesktopFolder' '[^']*\|Second CPack WiX Test' 'CM_SHORTCUT_DESKTOP_applications2' +-- diff --git a/Tests/RunCMake/CPack_WIX/RunCMakeTest.cmake b/Tests/RunCMake/CPack_WIX/RunCMakeTest.cmake index cf14d7d..816d949 100644 --- a/Tests/RunCMake/CPack_WIX/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack_WIX/RunCMakeTest.cmake @@ -7,6 +7,7 @@ set(RunCPack_GLOB *.msi) set(RunCPack_VERIFY powershell -ExecutionPolicy Bypass -File ${CMAKE_CURRENT_LIST_DIR}/print-msi.ps1) function(run_cpack_wix v) + set(RunCMake_TEST_OPTIONS -DCPACK_WIX_VERSION=${v}) run_cpack(${v}-AppWiX SAMPLE AppWiX BUILD) endfunction() @@ -14,3 +15,10 @@ if(CMake_TEST_CPACK_WIX3) set(ENV{PATH} "${CMake_TEST_CPACK_WIX3};${env_PATH}") run_cpack_wix(3) endif() + +if(CMake_TEST_CPACK_WIX4) + set(ENV{PATH} "${CMake_TEST_CPACK_WIX4};${env_PATH}") + set(ENV{WIX_EXTENSIONS} "${CMake_TEST_CPACK_WIX4}") + run_cpack_wix(4) + unset(ENV{WIX_EXTENSIONS}) +endif() |