CPack/WiX: Add support for perUser install scope

This allows one to create installers that install to `AppDataLocal`
and do not require admin privileges to execute.

Fixes: #24939
This commit is contained in:
Ivan Desiatov
2026-02-13 10:57:39 -05:00
committed by Brad King
parent 17aaf26a26
commit 781754f505
17 changed files with 350 additions and 30 deletions
+40 -12
View File
@@ -408,8 +408,9 @@ Windows using WiX.
In 32-bit builds the token will expand empty while in 64-bit builds
it will expand to ``64``.
When unset generated installers will default installing to
``ProgramFiles<64>Folder``.
When unset generated installers will install to
``LocalAppDataFolder`` if :variable:`CPACK_WIX_INSTALL_SCOPE` is ``perUser``,
and to ``ProgramFiles<64>Folder`` otherwise.
.. variable:: CPACK_WIX_ROOT
@@ -451,7 +452,7 @@ Windows using WiX.
.. versionadded:: 3.29
This variable can be optionally set to specify the ``InstallScope``
of the installer:
of the installer (see https://docs.firegiant.com/wix3/xsd/wix/package/):
``perMachine``
Create an installer that installs for all users and requires
@@ -461,7 +462,20 @@ Windows using WiX.
This is the default. See policy :policy:`CMP0172`.
``perUser``
Not yet supported. This is reserved for future use.
Create an installer that installs only for the current user
and does not require administrative privileges. Start menu entries created
by the installer are visible only to the current user.
To enable per-user installation, the installer has to generate some
additional registry entries to serve as "key paths" for installed
components (see https://learn.microsoft.com/en-us/windows/win32/msi/ice38).
These registry entries are created under ``HKEY_CURRENT_USER``, using the
path specified by the :variable:`CPACK_WIX_COMPONENT_KEYS_REGISTRY_PATH`
variable.
.. versionchanged:: 4.3
Before CMake 4.3, this value was reserved for future use and not supported.
``NONE``
Create an installer without any ``InstallScope`` attribute.
@@ -477,16 +491,30 @@ Windows using WiX.
but the start menu entry and uninstaller registration are created only
for the current user.
.. warning::
.. warning::
Installations performed by installers created with different
``InstallScope`` values cannot be cleanly updated or replaced by each other.
For example, to transition a project's installers from ``NONE`` to
``perMachine``, or from ``perMachine`` to ``perUser``, the latter installer
should be distributed with instructions to first manually uninstall
any older version.
An installation performed by an installer created without any
``InstallScope`` cannot be cleanly updated or replaced by an
installer with an ``InstallScope``. In order to transition
a project's installers from ``NONE`` to ``perMachine``, the
latter installer should be distributed with instructions to
first manually uninstall any older version.
.. variable:: CPACK_WIX_COMPONENT_KEYS_REGISTRY_PATH
See https://docs.firegiant.com/wix3/xsd/wix/package/
.. versionadded:: 4.3
This variable determines the registry path under ``HKEY_CURRENT_USER``
where the installer will create registry entries to serve as "key paths"
for components if :variable:`CPACK_WIX_INSTALL_SCOPE` is set to ``perUser``.
Use forward slashes (``/``) as path separators to avoid issues with escapes;
they will be converted to backslashes (``\``) by the WIX generator.
Default value is ``Software/<Vendor>/<PackageName>/Components``,
where ``<Vendor>`` is taken from :variable:`CPACK_PACKAGE_VENDOR`,
and ``<PackageName>`` is taken from :variable:`CPACK_PACKAGE_NAME`.
Example: ``Software/MyCompany/MyProduct/Components``
.. variable:: CPACK_WIX_CAB_PER_COMPONENT
+5
View File
@@ -0,0 +1,5 @@
cpack-wix-perUser
-----------------
* The :cpack_gen:`CPack WIX Generator` now supports per-user installers
by setting :variable:`CPACK_WIX_INSTALL_SCOPE` to ``perUser``.
+53 -4
View File
@@ -27,6 +27,7 @@
#include "cmWIXDirectoriesSourceWriter.h"
#include "cmWIXFeaturesSourceWriter.h"
#include "cmWIXFilesSourceWriter.h"
#include "cmWIXInstallScope.h"
#include "cmWIXRichTextFormatWriter.h"
#include "cmWIXSourceWriter.h"
@@ -545,9 +546,20 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles()
this->WixSources.push_back(directoryDefinitionsFilename);
cmWIXInstallScope installScope = GetInstallScope();
std::string componentKeysRegistryPath =
GetOption("CPACK_WIX_COMPONENT_KEYS_REGISTRY_PATH");
if (componentKeysRegistryPath.empty()) {
componentKeysRegistryPath =
cmStrCat("Software\\", GetOption("CPACK_PACKAGE_VENDOR"), "\\",
GetOption("CPACK_PACKAGE_NAME"), "\\Components");
}
cmSystemTools::ReplaceString(componentKeysRegistryPath, "/", "\\");
cmWIXDirectoriesSourceWriter directoryDefinitions(
this->WixVersion, this->Logger, directoryDefinitionsFilename,
this->ComponentGuidType);
this->ComponentGuidType, installScope, componentKeysRegistryPath);
InjectXmlNamespaces(directoryDefinitions);
directoryDefinitions.BeginElement("Fragment");
@@ -565,15 +577,18 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles()
auto installationPrefixDirectory =
directoryDefinitions.BeginInstallationPrefixDirectory(GetRootFolderId(),
installRoot);
cm::optional<std::string> removeFolderComponentId =
directoryDefinitions.EmitRemoveFolderComponentOnUserInstall(
"INSTALL_ROOT");
std::string fileDefinitionsFilename =
cmStrCat(this->CPackTopLevel, "/files.wxs");
this->WixSources.push_back(fileDefinitionsFilename);
cmWIXFilesSourceWriter fileDefinitions(this->WixVersion, this->Logger,
fileDefinitionsFilename,
this->ComponentGuidType);
cmWIXFilesSourceWriter fileDefinitions(
this->WixVersion, this->Logger, fileDefinitionsFilename,
this->ComponentGuidType, installScope, componentKeysRegistryPath);
InjectXmlNamespaces(fileDefinitions);
fileDefinitions.BeginElement("Fragment");
@@ -622,6 +637,10 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles()
*package, GetOption("CPACK_WIX_UPGRADE_GUID"));
}
if (removeFolderComponentId.has_value()) {
featureDefinitions.EmitComponentRef(*removeFolderComponentId);
}
if (!CreateFeatureHierarchy(featureDefinitions)) {
return false;
}
@@ -728,6 +747,8 @@ std::string cmCPackWIXGenerator::GetRootFolderId() const
cmValue rootFolderId = GetOption("CPACK_WIX_ROOT_FOLDER_ID");
if (rootFolderId) {
result = *rootFolderId;
} else if (GetInstallScope() == cmWIXInstallScope::PER_USER) {
result = "LocalAppDataFolder";
} else if (this->WixVersion >= 4) {
result = "ProgramFiles6432Folder";
} else {
@@ -1083,6 +1104,13 @@ void cmCPackWIXGenerator::AddDirectoryAndFileDefinitions(
directoryDefinitions.AddAttribute("Name", fileName);
this->Patch->ApplyFragment(subDirectoryId, directoryDefinitions);
cm::optional<std::string> removeFolderComponentId =
directoryDefinitions.EmitRemoveFolderComponentOnUserInstall(
subDirectoryId);
if (removeFolderComponentId.has_value()) {
featureDefinitions.EmitComponentRef(*removeFolderComponentId);
}
AddDirectoryAndFileDefinitions(fullPath, subDirectoryId,
directoryDefinitions, fileDefinitions,
featureDefinitions, packageExecutables,
@@ -1374,3 +1402,24 @@ void cmCPackWIXGenerator::InjectXmlNamespaces(cmWIXSourceWriter& sourceWriter)
ns.second);
}
}
cmWIXInstallScope cmCPackWIXGenerator::GetInstallScope() const
{
cmValue value = this->GetOption("CPACK_WIX_INSTALL_SCOPE");
if (value == "perUser"_s) {
return cmWIXInstallScope::PER_USER;
}
if (value == "perMachine"_s) {
return cmWIXInstallScope::PER_MACHINE;
}
if (value == "NONE"_s) {
return cmWIXInstallScope::NONE;
}
cmCPackLogger(
cmCPackLog::LOG_ERROR,
"Invalid CPACK_WIX_INSTALL_SCOPE value, defaulting to perMachine: "
<< (value ? *value : "") << std::endl);
return cmWIXInstallScope::PER_MACHINE;
}
+3
View File
@@ -7,6 +7,7 @@
#include <string>
#include "cmCPackGenerator.h"
#include "cmWIXInstallScope.h"
#include "cmWIXPatch.h"
#include "cmWIXShortcut.h"
@@ -159,6 +160,8 @@ private:
void InjectXmlNamespaces(cmWIXSourceWriter& sourceWriter);
cmWIXInstallScope GetInstallScope() const;
std::vector<std::string> WixSources;
id_map_t PathToIdMap;
ambiguity_map_t IdAmbiguityCounter;
@@ -6,9 +6,24 @@
cmWIXDirectoriesSourceWriter::cmWIXDirectoriesSourceWriter(
unsigned long wixVersion, cmCPackLog* logger, std::string const& filename,
GuidType componentGuidType)
GuidType componentGuidType, cmWIXInstallScope installScope,
std::string componentKeysRegistryPath)
: cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType)
, ComponentKeysRegistryPath(std::move(componentKeysRegistryPath))
{
switch (installScope) {
case cmWIXInstallScope::PER_USER:
this->PerUserInstall = true;
break;
case cmWIXInstallScope::PER_MACHINE:
case cmWIXInstallScope::NONE:
this->PerUserInstall = false;
break;
default:
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Unhandled install scope value, this is a CPack Bug.");
break;
}
}
void cmWIXDirectoriesSourceWriter::EmitStartMenuFolder(
@@ -47,6 +62,43 @@ void cmWIXDirectoriesSourceWriter::EmitStartupFolder()
EndElement_StandardDirectory();
}
cm::optional<std::string>
cmWIXDirectoriesSourceWriter::EmitRemoveFolderComponentOnUserInstall(
std::string const& directoryId)
{
if (!this->PerUserInstall) {
return {};
}
BeginElement("Component");
std::string componentId = directoryId + "_RemoveFolderComponent";
AddAttribute("Id", componentId);
AddAttribute("Guid", CreateGuidFromComponentId(componentId));
BeginElement("RemoveFolder");
AddAttribute("Id", directoryId + "_RemoveFolder");
AddAttribute("Directory", directoryId);
AddAttribute("On", "uninstall");
EndElement("RemoveFolder");
// Need a keyPath for the component
BeginElement("RegistryValue");
AddAttribute("Root", "HKCU");
AddAttribute("Key", this->ComponentKeysRegistryPath);
AddAttribute("Name", componentId);
AddAttribute("Type", "string");
AddAttribute("Value", "1");
AddAttribute("KeyPath", "yes");
EndElement("RegistryValue");
EndElement("Component");
return componentId;
}
cmWIXDirectoriesSourceWriter::InstallationPrefixDirectory
cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory(
std::string const& programFilesFolderId,
@@ -4,7 +4,10 @@
#include <string>
#include <cm/optional>
#include "cmCPackGenerator.h"
#include "cmWIXInstallScope.h"
#include "cmWIXSourceWriter.h"
/** \class cmWIXDirectoriesSourceWriter
@@ -15,7 +18,9 @@ class cmWIXDirectoriesSourceWriter : public cmWIXSourceWriter
public:
cmWIXDirectoriesSourceWriter(unsigned long wixVersion, cmCPackLog* logger,
std::string const& filename,
GuidType componentGuidType);
GuidType componentGuidType,
cmWIXInstallScope installScope,
std::string componentKeysRegistryPath);
void EmitStartMenuFolder(std::string const& startMenuFolder);
@@ -23,6 +28,9 @@ public:
void EmitStartupFolder();
cm::optional<std::string> EmitRemoveFolderComponentOnUserInstall(
std::string const& directoryId);
struct InstallationPrefixDirectory
{
bool HasStandardDirectory = false;
@@ -35,4 +43,8 @@ public:
void EndInstallationPrefixDirectory(
InstallationPrefixDirectory installationPrefixDirectory);
private:
bool PerUserInstall = false;
std::string ComponentKeysRegistryPath;
};
+42 -6
View File
@@ -19,12 +19,26 @@
# include "cmsys/Encoding.hxx"
#endif
cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(unsigned long wixVersion,
cmCPackLog* logger,
std::string const& filename,
GuidType componentGuidType)
cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(
unsigned long wixVersion, cmCPackLog* logger, std::string const& filename,
GuidType componentGuidType, cmWIXInstallScope installScope,
std::string componentKeysRegistryPath)
: cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType)
, ComponentKeysRegistryPath(std::move(componentKeysRegistryPath))
{
switch (installScope) {
case cmWIXInstallScope::PER_USER:
this->PerUserInstall = true;
break;
case cmWIXInstallScope::PER_MACHINE:
case cmWIXInstallScope::NONE:
this->PerUserInstall = false;
break;
default:
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Unhandled install scope value, this is a CPack Bug.");
break;
}
}
void cmWIXFilesSourceWriter::EmitShortcut(std::string const& id,
@@ -127,7 +141,11 @@ std::string cmWIXFilesSourceWriter::EmitComponentFile(
std::string componentId = std::string("CM_C") + id;
std::string fileId = std::string("CM_F") + id;
std::string guid = CreateGuidFromComponentId(componentId);
// Wix doesn't support automatic GUIDs for components which have both
// registry entries and files.
std::string guid = this->PerUserInstall
? CreateCmakeGeneratedGuidFromComponentId(componentId)
: CreateGuidFromComponentId(componentId);
BeginElement("DirectoryRef");
AddAttribute("Id", directoryId);
@@ -150,6 +168,22 @@ std::string cmWIXFilesSourceWriter::EmitComponentFile(
}
patch.ApplyFragment(componentId, *this);
if (this->PerUserInstall) {
// For perUser installs, MSI requires using a registry entry as the key
// path for components.
BeginElement("RegistryValue");
AddAttribute("Root", "HKCU");
AddAttribute("Key", this->ComponentKeysRegistryPath);
AddAttribute("Name", fileId);
AddAttribute("Type", "string");
AddAttribute("Value", "1");
AddAttribute("KeyPath", "yes");
EndElement("RegistryValue");
}
BeginElement("File");
AddAttribute("Id", fileId);
@@ -164,7 +198,9 @@ std::string cmWIXFilesSourceWriter::EmitComponentFile(
#endif
AddAttribute("Source", sourcePath);
AddAttribute("KeyPath", "yes");
if (!this->PerUserInstall) {
AddAttribute("KeyPath", "yes");
}
mode_t fileMode = 0;
cmSystemTools::GetPermissions(filePath.c_str(), fileMode);
+10 -1
View File
@@ -2,7 +2,10 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include <string>
#include "cmCPackGenerator.h"
#include "cmWIXInstallScope.h"
#include "cmWIXPatch.h"
#include "cmWIXShortcut.h"
#include "cmWIXSourceWriter.h"
@@ -15,7 +18,9 @@ class cmWIXFilesSourceWriter : public cmWIXSourceWriter
public:
cmWIXFilesSourceWriter(unsigned long wixVersion, cmCPackLog* logger,
std::string const& filename,
GuidType componentGuidType);
GuidType componentGuidType,
cmWIXInstallScope installScope,
std::string componentKeysRegistryPath);
void EmitShortcut(std::string const& id, cmWIXShortcut const& shortcut,
std::string const& shortcutPrefix, size_t shortcutIndex);
@@ -37,4 +42,8 @@ public:
std::string const& filePath, cmWIXPatch& patch,
cmInstalledFile const* installedFile,
int diskId);
private:
bool PerUserInstall = false;
std::string ComponentKeysRegistryPath;
};
+10
View File
@@ -0,0 +1,10 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
enum class cmWIXInstallScope
{
NONE,
PER_MACHINE,
PER_USER
};
+11 -5
View File
@@ -159,15 +159,21 @@ std::string cmWIXSourceWriter::CreateGuidFromComponentId(
{
std::string guid = "*";
if (this->ComponentGuidType == CMAKE_GENERATED_GUID) {
cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
std::string md5 = hasher.HashString(componentId);
cmUuid uuid;
std::vector<unsigned char> ns;
guid = uuid.FromMd5(ns, md5);
guid = CreateCmakeGeneratedGuidFromComponentId(componentId);
}
return guid;
}
std::string cmWIXSourceWriter::CreateCmakeGeneratedGuidFromComponentId(
std::string const& componentId)
{
cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
std::string md5 = hasher.HashString(componentId);
cmUuid uuid;
std::vector<unsigned char> ns;
return uuid.FromMd5(ns, md5);
}
void cmWIXSourceWriter::WriteXMLDeclaration()
{
File << R"(<?xml version="1.0" encoding="UTF-8"?>)" << std::endl;
+8
View File
@@ -5,6 +5,8 @@
#include <string>
#include <vector>
#include <cm/optional>
#include "cmsys/FStream.hxx"
#include "cmCPackLog.h"
@@ -52,6 +54,12 @@ public:
std::string CreateGuidFromComponentId(std::string const& componentId);
// In most cases CreateGuidFromComponentId should be used instead, since it
// takes ComponentGuidType into consideration. This function always generates
// an explicit GUID.
static std::string CreateCmakeGeneratedGuidFromComponentId(
std::string const& componentId);
static std::string EscapeAttributeValue(std::string const& value);
protected:
@@ -0,0 +1 @@
include(${RunCMake_SOURCE_DIR}/cpack-check-common.cmake)
@@ -0,0 +1,40 @@
-- MyLib-1\.0\.0-(win64|windows-arm64)\.msi
Component: 'INSTALL_ROOT_RemoveFolderComponent' 'INSTALL_ROOT'
Component: 'CM_DP_applications\.bin_RemoveFolderComponent' 'CM_DP_applications\.bin'
Component: 'CM_DP_applications2\.bin_RemoveFolderComponent' 'CM_DP_applications2\.bin'
Component: 'CM_DP_extras\.extras_RemoveFolderComponent' 'CM_DP_extras\.extras'
Component: 'CM_DP_extras\.extras\.empty_RemoveFolderComponent' 'CM_DP_extras\.extras\.empty'
Component: 'CM_DP_headers\.include_RemoveFolderComponent' 'CM_DP_headers\.include'
Component: 'CM_DP_libraries\.lib_RemoveFolderComponent' 'CM_DP_libraries\.lib'
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' 'LocalAppDataFolder' '[^']*\|CPack Component Example'
Directory: 'CM_DP_applications\.bin' 'INSTALL_ROOT' 'bin'
Directory: 'CM_DP_applications2\.bin' 'INSTALL_ROOT' 'bin'
Directory: 'CM_DP_extras\.extras' 'INSTALL_ROOT' 'extras'
Directory: 'CM_DP_extras\.extras\.empty' 'CM_DP_extras\.extras' 'empty'
Directory: 'CM_DP_headers\.include' 'INSTALL_ROOT' 'include'
Directory: 'CM_DP_libraries\.lib' 'INSTALL_ROOT' 'lib'
Directory: 'PROGRAM_MENU_FOLDER' 'ProgramMenuFolder' 'MyLib'
Directory: 'DesktopFolder' 'TARGETDIR' 'Desktop'
Directory: 'LocalAppDataFolder' 'TARGETDIR' '\.'
Directory: 'TARGETDIR' '' 'SourceDir'
Directory: 'ProgramMenuFolder' 'TARGETDIR' '\.'
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'
--
@@ -0,0 +1 @@
include(${RunCMake_SOURCE_DIR}/cpack-check-common.cmake)
@@ -0,0 +1,13 @@
CPack: Create package using WIX
CPack: Install projects
CPack: - Install project: CPackWiXGenerator \[Release\]
CPack: - Install component: applications
CPack: - Install component: applications2
CPack: - Install component: empty1
CPack: - Install component: empty2
CPack: - Install component: extras
CPack: - Install component: headers
CPack: - Install component: libraries
CPack: Create package
CPack: - package: [^
]*/Tests/RunCMake/CPack_WIX/4-AppWiX-perUser-build/MyLib-1\.0\.0-(win64|windows-arm64)\.msi generated\.
@@ -0,0 +1,40 @@
-- MyLib-1\.0\.0-(win64|windows-arm64)\.msi
Component: 'INSTALL_ROOT_RemoveFolderComponent' 'INSTALL_ROOT'
Component: 'CM_DP_applications\.bin_RemoveFolderComponent' 'CM_DP_applications\.bin'
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_DP_applications2\.bin_RemoveFolderComponent' 'CM_DP_applications2\.bin'
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_DP_extras\.extras_RemoveFolderComponent' 'CM_DP_extras\.extras'
Component: 'CM_DP_extras\.extras\.empty_RemoveFolderComponent' 'CM_DP_extras\.extras\.empty'
Component: 'CM_C_EMPTY_CM_DP_extras\.extras\.empty' 'CM_DP_extras\.extras\.empty'
Component: 'CM_DP_headers\.include_RemoveFolderComponent' 'CM_DP_headers\.include'
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_DP_libraries\.lib_RemoveFolderComponent' 'CM_DP_libraries\.lib'
Component: 'CM_CP_libraries\.lib\.mylib\.lib' 'CM_DP_libraries\.lib'
Directory: 'INSTALL_ROOT' 'LocalAppDataFolder' '[^']*\|CPack Component Example'
Directory: 'CM_DP_applications\.bin' 'INSTALL_ROOT' 'bin'
Directory: 'CM_DP_applications2\.bin' 'INSTALL_ROOT' 'bin'
Directory: 'CM_DP_extras\.extras' 'INSTALL_ROOT' 'extras'
Directory: 'CM_DP_extras\.extras\.empty' 'CM_DP_extras\.extras' 'empty'
Directory: 'CM_DP_headers\.include' 'INSTALL_ROOT' 'include'
Directory: 'CM_DP_libraries\.lib' 'INSTALL_ROOT' 'lib'
Directory: 'PROGRAM_MENU_FOLDER' 'ProgramMenuFolder' 'MyLib'
Directory: 'DesktopFolder' 'TARGETDIR' 'Desktop'
Directory: 'LocalAppDataFolder' 'TARGETDIR' 'LocalApp'
Directory: 'ProgramMenuFolder' 'TARGETDIR' 'PMenu'
Directory: 'TARGETDIR' '' 'SourceDir'
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'
--
@@ -11,14 +11,21 @@ function(run_cpack_wix v)
run_cpack(${v}-AppWiX SAMPLE AppWiX BUILD)
endfunction()
function(run_cpack_wix_peruser v)
set(RunCMake_TEST_OPTIONS -DCPACK_WIX_VERSION=${v} -DCPACK_WIX_INSTALL_SCOPE=perUser)
run_cpack(${v}-AppWiX-perUser SAMPLE AppWiX BUILD)
endfunction()
if(CMake_TEST_CPACK_WIX3)
set(ENV{PATH} "${CMake_TEST_CPACK_WIX3};${env_PATH}")
run_cpack_wix(3)
run_cpack_wix_peruser(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)
run_cpack_wix_peruser(4)
unset(ENV{WIX_EXTENSIONS})
endif()