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
This commit is contained in:
Brad King
2024-03-19 19:41:34 -04:00
parent cfe5bbdc54
commit 03884f4f32
22 changed files with 447 additions and 83 deletions

View File

@@ -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 "")

View File

@@ -1 +1,2 @@
& "$pwsh" -File .gitlab/ci/wix3.ps1
& "$pwsh" -File .gitlab/ci/wix4.ps1

View File

@@ -5,3 +5,4 @@ if ("$env:CMAKE_CI_NIGHTLY" -eq "true") {
}
& "$pwsh" -File .gitlab/ci/wix3.ps1
& "$pwsh" -File .gitlab/ci/wix4.ps1

View File

@@ -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

View File

@@ -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.

View File

@@ -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)

View File

@@ -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>

View File

@@ -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
{
if (GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER").IsOn()) {
return "";
}
std::string result;
std::string result = "ProgramFiles<64>Folder";
if (GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER").IsOn()) {
return result;
}
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), '"');

View File

@@ -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;
};

View File

@@ -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();
}
}

View File

@@ -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);
};

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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)
{
}

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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:

View File

@@ -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()

View File

@@ -0,0 +1 @@
include(${RunCMake_SOURCE_DIR}/cpack-check-common.cmake)

View File

@@ -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\.

View File

@@ -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'
--

View File

@@ -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()