summaryrefslogtreecommitdiffstats
path: root/Source/CPack/WiX
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CPack/WiX')
-rw-r--r--Source/CPack/WiX/cmCPackWIXGenerator.cxx1144
-rw-r--r--Source/CPack/WiX/cmCPackWIXGenerator.h159
-rw-r--r--Source/CPack/WiX/cmWIXAccessControlList.cxx126
-rw-r--r--Source/CPack/WiX/cmWIXAccessControlList.h34
-rw-r--r--Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx82
-rw-r--r--Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h34
-rw-r--r--Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx95
-rw-r--r--Source/CPack/WiX/cmWIXFeaturesSourceWriter.h32
-rw-r--r--Source/CPack/WiX/cmWIXFilesSourceWriter.cxx166
-rw-r--r--Source/CPack/WiX/cmWIXFilesSourceWriter.h43
-rw-r--r--Source/CPack/WiX/cmWIXPatch.cxx95
-rw-r--r--Source/CPack/WiX/cmWIXPatch.h37
-rw-r--r--Source/CPack/WiX/cmWIXPatchParser.cxx146
-rw-r--r--Source/CPack/WiX/cmWIXPatchParser.h90
-rw-r--r--Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx186
-rw-r--r--Source/CPack/WiX/cmWIXRichTextFormatWriter.h45
-rw-r--r--Source/CPack/WiX/cmWIXShortcut.cxx105
-rw-r--r--Source/CPack/WiX/cmWIXShortcut.h60
-rw-r--r--Source/CPack/WiX/cmWIXSourceWriter.cxx184
-rw-r--r--Source/CPack/WiX/cmWIXSourceWriter.h80
20 files changed, 2943 insertions, 0 deletions
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
new file mode 100644
index 0000000..2bccf2e
--- /dev/null
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -0,0 +1,1144 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCPackWIXGenerator.h"
+
+#include <CPack/cmCPackComponentGroup.h>
+#include <CPack/cmCPackLog.h>
+#include <algorithm>
+#include <cmCryptoHash.h>
+#include <cmGeneratedFileStream.h>
+#include <cmInstalledFile.h>
+#include <cmSystemTools.h>
+#include <cmUuid.h>
+
+#include "cmWIXDirectoriesSourceWriter.h"
+#include "cmWIXFeaturesSourceWriter.h"
+#include "cmWIXFilesSourceWriter.h"
+#include "cmWIXRichTextFormatWriter.h"
+#include "cmWIXSourceWriter.h"
+
+#include <cmsys/Directory.hxx>
+#include <cmsys/Encoding.hxx>
+#include <cmsys/FStream.hxx>
+#include <cmsys/SystemTools.hxx>
+
+#include <rpc.h> // for GUID generation
+
+cmCPackWIXGenerator::cmCPackWIXGenerator()
+ : Patch(0)
+ , ComponentGuidType(cmWIXSourceWriter::WIX_GENERATED_GUID)
+{
+}
+
+cmCPackWIXGenerator::~cmCPackWIXGenerator()
+{
+ if (this->Patch) {
+ delete this->Patch;
+ }
+}
+
+int cmCPackWIXGenerator::InitializeInternal()
+{
+ componentPackageMethod = ONE_PACKAGE;
+ this->Patch = new cmWIXPatch(this->Logger);
+
+ return this->Superclass::InitializeInternal();
+}
+
+bool cmCPackWIXGenerator::RunWiXCommand(std::string const& command)
+{
+ std::string logFileName = this->CPackTopLevel + "/wix.log";
+
+ cmCPackLogger(cmCPackLog::LOG_DEBUG, "Running WiX command: " << command
+ << std::endl);
+
+ std::string output;
+
+ int returnValue = 0;
+ bool status = cmSystemTools::RunSingleCommand(command.c_str(), &output,
+ &output, &returnValue, 0,
+ cmSystemTools::OUTPUT_NONE);
+
+ cmsys::ofstream logFile(logFileName.c_str(), std::ios::app);
+ logFile << command << std::endl;
+ logFile << output;
+ logFile.close();
+
+ if (!status || returnValue) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running WiX candle. "
+ "Please check '"
+ << logFileName << "' for errors." << std::endl);
+
+ return false;
+ }
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::RunCandleCommand(std::string const& sourceFile,
+ std::string const& objectFile)
+{
+ std::string executable;
+ if (!RequireOption("CPACK_WIX_CANDLE_EXECUTABLE", executable)) {
+ return false;
+ }
+
+ std::ostringstream command;
+ command << QuotePath(executable);
+ command << " -nologo";
+ command << " -arch " << GetArchitecture();
+ command << " -out " << QuotePath(objectFile);
+
+ for (extension_set_t::const_iterator i = CandleExtensions.begin();
+ i != CandleExtensions.end(); ++i) {
+ command << " -ext " << QuotePath(*i);
+ }
+
+ AddCustomFlags("CPACK_WIX_CANDLE_EXTRA_FLAGS", command);
+
+ command << " " << QuotePath(sourceFile);
+
+ return RunWiXCommand(command.str());
+}
+
+bool cmCPackWIXGenerator::RunLightCommand(std::string const& objectFiles)
+{
+ std::string executable;
+ if (!RequireOption("CPACK_WIX_LIGHT_EXECUTABLE", executable)) {
+ return false;
+ }
+
+ std::ostringstream command;
+ command << QuotePath(executable);
+ command << " -nologo";
+ command << " -out " << QuotePath(packageFileNames.at(0));
+
+ for (extension_set_t::const_iterator i = this->LightExtensions.begin();
+ i != this->LightExtensions.end(); ++i) {
+ command << " -ext " << QuotePath(*i);
+ }
+
+ const char* const cultures = GetOption("CPACK_WIX_CULTURES");
+ if (cultures) {
+ command << " -cultures:" << cultures;
+ }
+
+ AddCustomFlags("CPACK_WIX_LIGHT_EXTRA_FLAGS", command);
+
+ command << " " << objectFiles;
+
+ return RunWiXCommand(command.str());
+}
+
+int cmCPackWIXGenerator::PackageFiles()
+{
+ if (!PackageFilesImpl() || cmSystemTools::GetErrorOccuredFlag()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Fatal WiX Generator Error"
+ << std::endl);
+ return false;
+ }
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::InitializeWiXConfiguration()
+{
+ if (!ReadListFile("CPackWIX.cmake")) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while executing CPackWIX.cmake"
+ << std::endl);
+ return false;
+ }
+
+ if (GetOption("CPACK_WIX_PRODUCT_GUID") == 0) {
+ std::string guid = GenerateGUID();
+ SetOption("CPACK_WIX_PRODUCT_GUID", guid.c_str());
+
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE,
+ "CPACK_WIX_PRODUCT_GUID implicitly set to " << guid << " . "
+ << std::endl);
+ }
+
+ if (GetOption("CPACK_WIX_UPGRADE_GUID") == 0) {
+ std::string guid = GenerateGUID();
+ SetOption("CPACK_WIX_UPGRADE_GUID", guid.c_str());
+
+ cmCPackLogger(
+ cmCPackLog::LOG_WARNING, "CPACK_WIX_UPGRADE_GUID implicitly set to "
+ << guid << " . "
+ "Please refer to the documentation on how and why "
+ "you might want to set this explicitly."
+ << std::endl);
+ }
+
+ if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY", this->CPackTopLevel)) {
+ return false;
+ }
+
+ if (GetOption("CPACK_WIX_LICENSE_RTF") == 0) {
+ std::string licenseFilename = this->CPackTopLevel + "/License.rtf";
+ SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename.c_str());
+
+ if (!CreateLicenseFile()) {
+ return false;
+ }
+ }
+
+ if (GetOption("CPACK_PACKAGE_VENDOR") == 0) {
+ std::string defaultVendor = "Humanity";
+ SetOption("CPACK_PACKAGE_VENDOR", defaultVendor.c_str());
+
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE,
+ "CPACK_PACKAGE_VENDOR implicitly set to "
+ << defaultVendor << " . " << std::endl);
+ }
+
+ if (GetOption("CPACK_WIX_UI_REF") == 0) {
+ std::string defaultRef = "WixUI_InstallDir";
+
+ if (!this->Components.empty()) {
+ defaultRef = "WixUI_FeatureTree";
+ }
+
+ SetOption("CPACK_WIX_UI_REF", defaultRef.c_str());
+ }
+
+ const char* packageContact = GetOption("CPACK_PACKAGE_CONTACT");
+ if (packageContact != 0 && GetOption("CPACK_WIX_PROPERTY_ARPCONTACT") == 0) {
+ SetOption("CPACK_WIX_PROPERTY_ARPCONTACT", packageContact);
+ }
+
+ CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions);
+ CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions);
+
+ this->LightExtensions.insert("WixUIExtension");
+ CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions);
+ CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions);
+
+ const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE");
+ if (patchFilePath) {
+ std::vector<std::string> patchFilePaths;
+ cmSystemTools::ExpandListArgument(patchFilePath, patchFilePaths);
+
+ for (size_t i = 0; i < patchFilePaths.size(); ++i) {
+ if (!this->Patch->LoadFragments(patchFilePaths[i])) {
+ return false;
+ }
+ }
+ }
+
+ // if install folder is supposed to be set absolutely, the default
+ // component guid "*" cannot be used
+ if (cmSystemTools::IsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) {
+ this->ComponentGuidType = cmWIXSourceWriter::CMAKE_GENERATED_GUID;
+ }
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::PackageFilesImpl()
+{
+ if (!InitializeWiXConfiguration()) {
+ return false;
+ }
+
+ CreateWiXVariablesIncludeFile();
+ CreateWiXPropertiesIncludeFile();
+ CreateWiXProductFragmentIncludeFile();
+
+ if (!CreateWiXSourceFiles()) {
+ return false;
+ }
+
+ AppendUserSuppliedExtraSources();
+
+ std::set<std::string> usedBaseNames;
+
+ std::ostringstream objectFiles;
+ for (size_t i = 0; i < this->WixSources.size(); ++i) {
+ std::string const& sourceFilename = this->WixSources[i];
+
+ std::string baseName =
+ cmSystemTools::GetFilenameWithoutLastExtension(sourceFilename);
+
+ unsigned int counter = 0;
+ std::string uniqueBaseName = baseName;
+
+ while (usedBaseNames.find(uniqueBaseName) != usedBaseNames.end()) {
+ std::ostringstream tmp;
+ tmp << baseName << ++counter;
+ uniqueBaseName = tmp.str();
+ }
+
+ usedBaseNames.insert(uniqueBaseName);
+
+ std::string objectFilename =
+ this->CPackTopLevel + "/" + uniqueBaseName + ".wixobj";
+
+ if (!RunCandleCommand(sourceFilename, objectFilename)) {
+ return false;
+ }
+
+ objectFiles << " " << QuotePath(objectFilename);
+ }
+
+ AppendUserSuppliedExtraObjects(objectFiles);
+
+ return RunLightCommand(objectFiles.str());
+}
+
+void cmCPackWIXGenerator::AppendUserSuppliedExtraSources()
+{
+ const char* cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES");
+ if (!cpackWixExtraSources)
+ return;
+
+ cmSystemTools::ExpandListArgument(cpackWixExtraSources, this->WixSources);
+}
+
+void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream)
+{
+ const char* cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS");
+ if (!cpackWixExtraObjects)
+ return;
+
+ std::vector<std::string> expandedExtraObjects;
+
+ cmSystemTools::ExpandListArgument(cpackWixExtraObjects,
+ expandedExtraObjects);
+
+ for (size_t i = 0; i < expandedExtraObjects.size(); ++i) {
+ stream << " " << QuotePath(expandedExtraObjects[i]);
+ }
+}
+
+void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile()
+{
+ std::string includeFilename = this->CPackTopLevel + "/cpack_variables.wxi";
+
+ cmWIXSourceWriter includeFile(this->Logger, includeFilename,
+ this->ComponentGuidType,
+ cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT);
+
+ CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_GUID");
+ CopyDefinition(includeFile, "CPACK_WIX_UPGRADE_GUID");
+ CopyDefinition(includeFile, "CPACK_PACKAGE_VENDOR");
+ CopyDefinition(includeFile, "CPACK_PACKAGE_NAME");
+ CopyDefinition(includeFile, "CPACK_PACKAGE_VERSION");
+ CopyDefinition(includeFile, "CPACK_WIX_LICENSE_RTF");
+ CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_ICON");
+ CopyDefinition(includeFile, "CPACK_WIX_UI_BANNER");
+ CopyDefinition(includeFile, "CPACK_WIX_UI_DIALOG");
+ SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER",
+ GetOption("CPACK_PACKAGE_NAME"));
+ CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER");
+ CopyDefinition(includeFile, "CPACK_WIX_UI_REF");
+}
+
+void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile()
+{
+ std::string includeFilename = this->CPackTopLevel + "/properties.wxi";
+
+ cmWIXSourceWriter includeFile(this->Logger, includeFilename,
+ this->ComponentGuidType,
+ cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT);
+
+ std::string prefix = "CPACK_WIX_PROPERTY_";
+ std::vector<std::string> options = GetOptions();
+
+ for (size_t i = 0; i < options.size(); ++i) {
+ std::string const& name = options[i];
+
+ if (name.length() > prefix.length() &&
+ name.substr(0, prefix.length()) == prefix) {
+ std::string id = name.substr(prefix.length());
+ std::string value = GetOption(name.c_str());
+
+ includeFile.BeginElement("Property");
+ includeFile.AddAttribute("Id", id);
+ includeFile.AddAttribute("Value", value);
+ includeFile.EndElement("Property");
+ }
+ }
+
+ if (GetOption("CPACK_WIX_PROPERTY_ARPINSTALLLOCATION") == 0) {
+ includeFile.BeginElement("Property");
+ includeFile.AddAttribute("Id", "INSTALL_ROOT");
+ includeFile.AddAttribute("Secure", "yes");
+
+ includeFile.BeginElement("RegistrySearch");
+ includeFile.AddAttribute("Id", "FindInstallLocation");
+ includeFile.AddAttribute("Root", "HKLM");
+ includeFile.AddAttribute(
+ "Key", "Software\\Microsoft\\Windows\\"
+ "CurrentVersion\\Uninstall\\[WIX_UPGRADE_DETECTED]");
+ includeFile.AddAttribute("Name", "InstallLocation");
+ includeFile.AddAttribute("Type", "raw");
+ includeFile.EndElement("RegistrySearch");
+ includeFile.EndElement("Property");
+
+ includeFile.BeginElement("SetProperty");
+ includeFile.AddAttribute("Id", "ARPINSTALLLOCATION");
+ includeFile.AddAttribute("Value", "[INSTALL_ROOT]");
+ includeFile.AddAttribute("After", "CostFinalize");
+ includeFile.EndElement("SetProperty");
+ }
+}
+
+void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile()
+{
+ std::string includeFilename = this->CPackTopLevel + "/product_fragment.wxi";
+
+ cmWIXSourceWriter includeFile(this->Logger, includeFilename,
+ this->ComponentGuidType,
+ cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT);
+
+ this->Patch->ApplyFragment("#PRODUCT", includeFile);
+}
+
+void cmCPackWIXGenerator::CopyDefinition(cmWIXSourceWriter& source,
+ std::string const& name)
+{
+ const char* value = GetOption(name.c_str());
+ if (value) {
+ AddDefinition(source, name, value);
+ }
+}
+
+void cmCPackWIXGenerator::AddDefinition(cmWIXSourceWriter& source,
+ std::string const& name,
+ std::string const& value)
+{
+ std::ostringstream tmp;
+ tmp << name << "=\"" << value << '"';
+
+ source.AddProcessingInstruction("define", tmp.str());
+}
+
+bool cmCPackWIXGenerator::CreateWiXSourceFiles()
+{
+ // if install folder is supposed to be set absolutely, the default
+ // component guid "*" cannot be used
+ std::string directoryDefinitionsFilename =
+ this->CPackTopLevel + "/directories.wxs";
+
+ this->WixSources.push_back(directoryDefinitionsFilename);
+
+ cmWIXDirectoriesSourceWriter directoryDefinitions(
+ this->Logger, directoryDefinitionsFilename, this->ComponentGuidType);
+ directoryDefinitions.BeginElement("Fragment");
+
+ std::string installRoot;
+ if (!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY", installRoot)) {
+ return false;
+ }
+
+ directoryDefinitions.BeginElement("Directory");
+ directoryDefinitions.AddAttribute("Id", "TARGETDIR");
+ directoryDefinitions.AddAttribute("Name", "SourceDir");
+
+ size_t installRootSize =
+ directoryDefinitions.BeginInstallationPrefixDirectory(
+ GetProgramFilesFolderId(), installRoot);
+
+ std::string fileDefinitionsFilename = this->CPackTopLevel + "/files.wxs";
+
+ this->WixSources.push_back(fileDefinitionsFilename);
+
+ cmWIXFilesSourceWriter fileDefinitions(this->Logger, fileDefinitionsFilename,
+ this->ComponentGuidType);
+
+ fileDefinitions.BeginElement("Fragment");
+
+ std::string featureDefinitionsFilename =
+ this->CPackTopLevel + "/features.wxs";
+
+ this->WixSources.push_back(featureDefinitionsFilename);
+
+ cmWIXFeaturesSourceWriter featureDefinitions(
+ this->Logger, featureDefinitionsFilename, this->ComponentGuidType);
+
+ featureDefinitions.BeginElement("Fragment");
+
+ featureDefinitions.BeginElement("Feature");
+ featureDefinitions.AddAttribute("Id", "ProductFeature");
+ featureDefinitions.AddAttribute("Display", "expand");
+ featureDefinitions.AddAttribute("Absent", "disallow");
+ featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT");
+
+ std::string cpackPackageName;
+ if (!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) {
+ return false;
+ }
+
+ std::string featureTitle = cpackPackageName;
+ if (const char* title = GetOption("CPACK_WIX_ROOT_FEATURE_TITLE")) {
+ featureTitle = title;
+ }
+ featureDefinitions.AddAttribute("Title", featureTitle);
+ if (const char* desc = GetOption("CPACK_WIX_ROOT_FEATURE_DESCRIPTION")) {
+ featureDefinitions.AddAttribute("Description", desc);
+ }
+ featureDefinitions.AddAttribute("Level", "1");
+ this->Patch->ApplyFragment("#PRODUCTFEATURE", featureDefinitions);
+
+ const char* package = GetOption("CPACK_WIX_CMAKE_PACKAGE_REGISTRY");
+ if (package) {
+ featureDefinitions.CreateCMakePackageRegistryEntry(
+ package, GetOption("CPACK_WIX_UPGRADE_GUID"));
+ }
+
+ if (!CreateFeatureHierarchy(featureDefinitions)) {
+ return false;
+ }
+
+ featureDefinitions.EndElement("Feature");
+
+ std::set<cmWIXShortcuts::Type> emittedShortcutTypes;
+
+ cmWIXShortcuts globalShortcuts;
+ if (Components.empty()) {
+ AddComponentsToFeature(toplevel, "ProductFeature", directoryDefinitions,
+ fileDefinitions, featureDefinitions,
+ globalShortcuts);
+
+ globalShortcuts.AddShortcutTypes(emittedShortcutTypes);
+ } else {
+ for (std::map<std::string, cmCPackComponent>::const_iterator i =
+ this->Components.begin();
+ i != this->Components.end(); ++i) {
+ cmCPackComponent const& component = i->second;
+
+ std::string componentPath = toplevel;
+ componentPath += "/";
+ componentPath += component.Name;
+
+ std::string componentFeatureId = "CM_C_" + component.Name;
+
+ cmWIXShortcuts featureShortcuts;
+ AddComponentsToFeature(componentPath, componentFeatureId,
+ directoryDefinitions, fileDefinitions,
+ featureDefinitions, featureShortcuts);
+
+ featureShortcuts.AddShortcutTypes(emittedShortcutTypes);
+
+ if (!CreateShortcuts(component.Name, componentFeatureId,
+ featureShortcuts, false, fileDefinitions,
+ featureDefinitions)) {
+ return false;
+ }
+ }
+ }
+
+ bool emitUninstallShortcut =
+ emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) !=
+ emittedShortcutTypes.end();
+
+ if (!CreateShortcuts(std::string(), "ProductFeature", globalShortcuts,
+ emitUninstallShortcut, fileDefinitions,
+ featureDefinitions)) {
+ return false;
+ }
+
+ featureDefinitions.EndElement("Fragment");
+ fileDefinitions.EndElement("Fragment");
+
+ directoryDefinitions.EndInstallationPrefixDirectory(installRootSize);
+
+ if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) !=
+ emittedShortcutTypes.end()) {
+ directoryDefinitions.EmitStartMenuFolder(
+ GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER"));
+ }
+
+ if (emittedShortcutTypes.find(cmWIXShortcuts::DESKTOP) !=
+ emittedShortcutTypes.end()) {
+ directoryDefinitions.EmitDesktopFolder();
+ }
+
+ if (emittedShortcutTypes.find(cmWIXShortcuts::STARTUP) !=
+ emittedShortcutTypes.end()) {
+ directoryDefinitions.EmitStartupFolder();
+ }
+
+ directoryDefinitions.EndElement("Directory");
+ directoryDefinitions.EndElement("Fragment");
+
+ if (!GenerateMainSourceFileFromTemplate()) {
+ return false;
+ }
+
+ return this->Patch->CheckForUnappliedFragments();
+}
+
+std::string cmCPackWIXGenerator::GetProgramFilesFolderId() const
+{
+ if (cmSystemTools::IsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) {
+ return "";
+ }
+ if (GetArchitecture() == "x86") {
+ return "ProgramFilesFolder";
+ } else {
+ return "ProgramFiles64Folder";
+ }
+}
+
+bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate()
+{
+ std::string wixTemplate = FindTemplate("WIX.template.in");
+ if (GetOption("CPACK_WIX_TEMPLATE") != 0) {
+ wixTemplate = GetOption("CPACK_WIX_TEMPLATE");
+ }
+
+ if (wixTemplate.empty()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Could not find CPack WiX template file WIX.template.in"
+ << std::endl);
+ return false;
+ }
+
+ std::string mainSourceFilePath = this->CPackTopLevel + "/main.wxs";
+
+ if (!ConfigureFile(wixTemplate.c_str(), mainSourceFilePath.c_str())) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Failed creating '"
+ << mainSourceFilePath << "'' from template." << std::endl);
+
+ return false;
+ }
+
+ this->WixSources.push_back(mainSourceFilePath);
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::CreateFeatureHierarchy(
+ cmWIXFeaturesSourceWriter& featureDefinitions)
+{
+ for (std::map<std::string, cmCPackComponentGroup>::const_iterator i =
+ ComponentGroups.begin();
+ i != ComponentGroups.end(); ++i) {
+ cmCPackComponentGroup const& group = i->second;
+ if (group.ParentGroup == 0) {
+ featureDefinitions.EmitFeatureForComponentGroup(group, *this->Patch);
+ }
+ }
+
+ for (std::map<std::string, cmCPackComponent>::const_iterator i =
+ this->Components.begin();
+ i != this->Components.end(); ++i) {
+ cmCPackComponent const& component = i->second;
+
+ if (!component.Group) {
+ featureDefinitions.EmitFeatureForComponent(component, *this->Patch);
+ }
+ }
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::AddComponentsToFeature(
+ std::string const& rootPath, std::string const& featureId,
+ cmWIXDirectoriesSourceWriter& directoryDefinitions,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions, cmWIXShortcuts& shortcuts)
+{
+ featureDefinitions.BeginElement("FeatureRef");
+ featureDefinitions.AddAttribute("Id", featureId);
+
+ std::vector<std::string> cpackPackageExecutablesList;
+ const char* cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES");
+ if (cpackPackageExecutables) {
+ cmSystemTools::ExpandListArgument(cpackPackageExecutables,
+ cpackPackageExecutablesList);
+ if (cpackPackageExecutablesList.size() % 2 != 0) {
+ cmCPackLogger(
+ cmCPackLog::LOG_ERROR,
+ "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
+ "<text label>."
+ << std::endl);
+ return false;
+ }
+ }
+
+ std::vector<std::string> cpackPackageDesktopLinksList;
+ const char* cpackPackageDesktopLinks =
+ GetOption("CPACK_CREATE_DESKTOP_LINKS");
+ if (cpackPackageDesktopLinks) {
+ cmSystemTools::ExpandListArgument(cpackPackageDesktopLinks,
+ cpackPackageDesktopLinksList);
+ }
+
+ AddDirectoryAndFileDefinitons(rootPath, "INSTALL_ROOT", directoryDefinitions,
+ fileDefinitions, featureDefinitions,
+ cpackPackageExecutablesList,
+ cpackPackageDesktopLinksList, shortcuts);
+
+ featureDefinitions.EndElement("FeatureRef");
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::CreateShortcuts(
+ std::string const& cpackComponentName, std::string const& featureId,
+ cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions)
+{
+ if (!shortcuts.empty(cmWIXShortcuts::START_MENU)) {
+ if (!this->CreateShortcutsOfSpecificType(
+ cmWIXShortcuts::START_MENU, cpackComponentName, featureId, "",
+ shortcuts, emitUninstallShortcut, fileDefinitions,
+ featureDefinitions)) {
+ return false;
+ }
+ }
+
+ if (!shortcuts.empty(cmWIXShortcuts::DESKTOP)) {
+ if (!this->CreateShortcutsOfSpecificType(
+ cmWIXShortcuts::DESKTOP, cpackComponentName, featureId, "DESKTOP",
+ shortcuts, false, fileDefinitions, featureDefinitions)) {
+ return false;
+ }
+ }
+
+ if (!shortcuts.empty(cmWIXShortcuts::STARTUP)) {
+ if (!this->CreateShortcutsOfSpecificType(
+ cmWIXShortcuts::STARTUP, cpackComponentName, featureId, "STARTUP",
+ shortcuts, false, fileDefinitions, featureDefinitions)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::CreateShortcutsOfSpecificType(
+ cmWIXShortcuts::Type type, std::string const& cpackComponentName,
+ std::string const& featureId, std::string const& idPrefix,
+ cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions)
+{
+ std::string directoryId;
+ switch (type) {
+ case cmWIXShortcuts::START_MENU:
+ directoryId = "PROGRAM_MENU_FOLDER";
+ break;
+ case cmWIXShortcuts::DESKTOP:
+ directoryId = "DesktopFolder";
+ break;
+ case cmWIXShortcuts::STARTUP:
+ directoryId = "StartupFolder";
+ break;
+ default:
+ return false;
+ }
+
+ featureDefinitions.BeginElement("FeatureRef");
+ featureDefinitions.AddAttribute("Id", featureId);
+
+ std::string cpackVendor;
+ if (!RequireOption("CPACK_PACKAGE_VENDOR", cpackVendor)) {
+ return false;
+ }
+
+ std::string cpackPackageName;
+ if (!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) {
+ return false;
+ }
+
+ std::string idSuffix;
+ if (!cpackComponentName.empty()) {
+ idSuffix += "_";
+ idSuffix += cpackComponentName;
+ }
+
+ std::string componentId = "CM_SHORTCUT";
+ if (idPrefix.size()) {
+ componentId += "_" + idPrefix;
+ }
+
+ componentId += idSuffix;
+
+ fileDefinitions.BeginElement("DirectoryRef");
+ fileDefinitions.AddAttribute("Id", directoryId);
+
+ fileDefinitions.BeginElement("Component");
+ fileDefinitions.AddAttribute("Id", componentId);
+ fileDefinitions.AddAttribute(
+ "Guid", fileDefinitions.CreateGuidFromComponentId(componentId));
+
+ this->Patch->ApplyFragment(componentId, fileDefinitions);
+
+ std::string registryKey =
+ std::string("Software\\") + cpackVendor + "\\" + cpackPackageName;
+
+ shortcuts.EmitShortcuts(type, registryKey, cpackComponentName,
+ fileDefinitions);
+
+ if (type == cmWIXShortcuts::START_MENU) {
+ fileDefinitions.EmitRemoveFolder("CM_REMOVE_PROGRAM_MENU_FOLDER" +
+ idSuffix);
+ }
+
+ if (emitUninstallShortcut) {
+ fileDefinitions.EmitUninstallShortcut(cpackPackageName);
+ }
+
+ fileDefinitions.EndElement("Component");
+ fileDefinitions.EndElement("DirectoryRef");
+
+ featureDefinitions.EmitComponentRef(componentId);
+ featureDefinitions.EndElement("FeatureRef");
+
+ return true;
+}
+
+bool cmCPackWIXGenerator::CreateLicenseFile()
+{
+ std::string licenseSourceFilename;
+ if (!RequireOption("CPACK_RESOURCE_FILE_LICENSE", licenseSourceFilename)) {
+ return false;
+ }
+
+ std::string licenseDestinationFilename;
+ if (!RequireOption("CPACK_WIX_LICENSE_RTF", licenseDestinationFilename)) {
+ return false;
+ }
+
+ std::string extension = GetRightmostExtension(licenseSourceFilename);
+
+ if (extension == ".rtf") {
+ cmSystemTools::CopyAFile(licenseSourceFilename.c_str(),
+ licenseDestinationFilename.c_str());
+ } else if (extension == ".txt") {
+ cmWIXRichTextFormatWriter rtfWriter(licenseDestinationFilename);
+
+ cmsys::ifstream licenseSource(licenseSourceFilename.c_str());
+
+ std::string line;
+ while (std::getline(licenseSource, line)) {
+ rtfWriter.AddText(line);
+ rtfWriter.AddText("\n");
+ }
+ } else {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "unsupported WiX License file extension '"
+ << extension << "'" << std::endl);
+
+ return false;
+ }
+
+ return true;
+}
+
+void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons(
+ std::string const& topdir, std::string const& directoryId,
+ cmWIXDirectoriesSourceWriter& directoryDefinitions,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions,
+ std::vector<std::string> const& packageExecutables,
+ std::vector<std::string> const& desktopExecutables,
+ cmWIXShortcuts& shortcuts)
+{
+ cmsys::Directory dir;
+ dir.Load(topdir.c_str());
+
+ std::string relativeDirectoryPath =
+ cmSystemTools::RelativePath(toplevel.c_str(), topdir.c_str());
+
+ if (relativeDirectoryPath.empty()) {
+ relativeDirectoryPath = ".";
+ }
+
+ cmInstalledFile const* directoryInstalledFile = this->GetInstalledFile(
+ this->RelativePathWithoutComponentPrefix(relativeDirectoryPath));
+
+ bool emptyDirectory = dir.GetNumberOfFiles() == 2;
+ bool createDirectory = false;
+
+ if (emptyDirectory) {
+ createDirectory = true;
+ }
+
+ if (directoryInstalledFile) {
+ if (directoryInstalledFile->HasProperty("CPACK_WIX_ACL")) {
+ createDirectory = true;
+ }
+ }
+
+ if (createDirectory) {
+ std::string componentId = fileDefinitions.EmitComponentCreateFolder(
+ directoryId, GenerateGUID(), directoryInstalledFile);
+ featureDefinitions.EmitComponentRef(componentId);
+ }
+
+ if (emptyDirectory) {
+ return;
+ }
+
+ for (size_t i = 0; i < dir.GetNumberOfFiles(); ++i) {
+ std::string fileName = dir.GetFile(static_cast<unsigned long>(i));
+
+ if (fileName == "." || fileName == "..") {
+ continue;
+ }
+
+ std::string fullPath = topdir + "/" + fileName;
+
+ std::string relativePath =
+ cmSystemTools::RelativePath(toplevel.c_str(), fullPath.c_str());
+
+ std::string id = PathToId(relativePath);
+
+ if (cmSystemTools::FileIsDirectory(fullPath.c_str())) {
+ std::string subDirectoryId = std::string("CM_D") + id;
+
+ directoryDefinitions.BeginElement("Directory");
+ directoryDefinitions.AddAttribute("Id", subDirectoryId);
+ directoryDefinitions.AddAttribute("Name", fileName);
+
+ AddDirectoryAndFileDefinitons(
+ fullPath, subDirectoryId, directoryDefinitions, fileDefinitions,
+ featureDefinitions, packageExecutables, desktopExecutables, shortcuts);
+
+ this->Patch->ApplyFragment(subDirectoryId, directoryDefinitions);
+ directoryDefinitions.EndElement("Directory");
+ } else {
+ cmInstalledFile const* installedFile = this->GetInstalledFile(
+ this->RelativePathWithoutComponentPrefix(relativePath));
+
+ if (installedFile) {
+ shortcuts.CreateFromProperties(id, directoryId, *installedFile);
+ }
+
+ std::string componentId = fileDefinitions.EmitComponentFile(
+ directoryId, id, fullPath, *(this->Patch), installedFile);
+
+ featureDefinitions.EmitComponentRef(componentId);
+
+ for (size_t j = 0; j < packageExecutables.size(); ++j) {
+ std::string const& executableName = packageExecutables[j++];
+ std::string const& textLabel = packageExecutables[j];
+
+ if (cmSystemTools::LowerCase(fileName) ==
+ cmSystemTools::LowerCase(executableName) + ".exe") {
+ cmWIXShortcut shortcut;
+ shortcut.label = textLabel;
+ shortcut.workingDirectoryId = directoryId;
+ shortcuts.insert(cmWIXShortcuts::START_MENU, id, shortcut);
+
+ if (!desktopExecutables.empty() &&
+ std::find(desktopExecutables.begin(), desktopExecutables.end(),
+ executableName) != desktopExecutables.end()) {
+ shortcuts.insert(cmWIXShortcuts::DESKTOP, id, shortcut);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool cmCPackWIXGenerator::RequireOption(std::string const& name,
+ std::string& value) const
+{
+ const char* tmp = GetOption(name.c_str());
+ if (tmp) {
+ value = tmp;
+
+ return true;
+ } else {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Required variable "
+ << name << " not set" << std::endl);
+
+ return false;
+ }
+}
+
+std::string cmCPackWIXGenerator::GetArchitecture() const
+{
+ std::string void_p_size;
+ RequireOption("CPACK_WIX_SIZEOF_VOID_P", void_p_size);
+
+ if (void_p_size == "8") {
+ return "x64";
+ } else {
+ return "x86";
+ }
+}
+
+std::string cmCPackWIXGenerator::GenerateGUID()
+{
+ UUID guid;
+ UuidCreate(&guid);
+
+ unsigned short* tmp = 0;
+ UuidToStringW(&guid, &tmp);
+
+ std::string result =
+ cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(tmp));
+ RpcStringFreeW(&tmp);
+
+ return cmSystemTools::UpperCase(result);
+}
+
+std::string cmCPackWIXGenerator::QuotePath(std::string const& path)
+{
+ return std::string("\"") + path + '"';
+}
+
+std::string cmCPackWIXGenerator::GetRightmostExtension(
+ std::string const& filename)
+{
+ std::string extension;
+
+ std::string::size_type i = filename.rfind(".");
+ if (i != std::string::npos) {
+ extension = filename.substr(i);
+ }
+
+ return cmSystemTools::LowerCase(extension);
+}
+
+std::string cmCPackWIXGenerator::PathToId(std::string const& path)
+{
+ id_map_t::const_iterator i = PathToIdMap.find(path);
+ if (i != PathToIdMap.end())
+ return i->second;
+
+ std::string id = CreateNewIdForPath(path);
+ return id;
+}
+
+std::string cmCPackWIXGenerator::CreateNewIdForPath(std::string const& path)
+{
+ std::vector<std::string> components;
+ cmSystemTools::SplitPath(path.c_str(), components, false);
+
+ size_t replacementCount = 0;
+
+ std::string identifier;
+ std::string currentComponent;
+
+ for (size_t i = 1; i < components.size(); ++i) {
+ if (i != 1)
+ identifier += '.';
+
+ currentComponent =
+ NormalizeComponentForId(components[i], replacementCount);
+
+ identifier += currentComponent;
+ }
+
+ std::string idPrefix = "P";
+ size_t replacementPercent = replacementCount * 100 / identifier.size();
+ if (replacementPercent > 33 || identifier.size() > 60) {
+ identifier = CreateHashedId(path, currentComponent);
+ idPrefix = "H";
+ }
+
+ std::ostringstream result;
+ result << idPrefix << "_" << identifier;
+
+ size_t ambiguityCount = ++IdAmbiguityCounter[identifier];
+
+ if (ambiguityCount > 999) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Error while trying to generate a unique Id for '"
+ << path << "'" << std::endl);
+
+ return std::string();
+ } else if (ambiguityCount > 1) {
+ result << "_" << ambiguityCount;
+ }
+
+ std::string resultString = result.str();
+
+ PathToIdMap[path] = resultString;
+
+ return resultString;
+}
+
+std::string cmCPackWIXGenerator::CreateHashedId(
+ std::string const& path, std::string const& normalizedFilename)
+{
+ cmCryptoHash sha1(cmCryptoHash::AlgoSHA1);
+ std::string const hash = sha1.HashString(path);
+
+ std::string identifier;
+ identifier += hash.substr(0, 7) + "_";
+
+ const size_t maxFileNameLength = 52;
+ if (normalizedFilename.length() > maxFileNameLength) {
+ identifier += normalizedFilename.substr(0, maxFileNameLength - 3);
+ identifier += "...";
+ } else {
+ identifier += normalizedFilename;
+ }
+
+ return identifier;
+}
+
+std::string cmCPackWIXGenerator::NormalizeComponentForId(
+ std::string const& component, size_t& replacementCount)
+{
+ std::string result;
+ result.resize(component.size());
+
+ for (size_t i = 0; i < component.size(); ++i) {
+ char c = component[i];
+ if (IsLegalIdCharacter(c)) {
+ result[i] = c;
+ } else {
+ result[i] = '_';
+ ++replacementCount;
+ }
+ }
+
+ return result;
+}
+
+bool cmCPackWIXGenerator::IsLegalIdCharacter(char c)
+{
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || c == '_' || c == '.';
+}
+
+void cmCPackWIXGenerator::CollectExtensions(std::string const& variableName,
+ extension_set_t& extensions)
+{
+ const char* variableContent = GetOption(variableName.c_str());
+ if (!variableContent)
+ return;
+
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(variableContent, list);
+ extensions.insert(list.begin(), list.end());
+}
+
+void cmCPackWIXGenerator::AddCustomFlags(std::string const& variableName,
+ std::ostream& stream)
+{
+ const char* variableContent = GetOption(variableName.c_str());
+ if (!variableContent)
+ return;
+
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(variableContent, list);
+
+ for (std::vector<std::string>::const_iterator i = list.begin();
+ i != list.end(); ++i) {
+ stream << " " << QuotePath(*i);
+ }
+}
+
+std::string cmCPackWIXGenerator::RelativePathWithoutComponentPrefix(
+ std::string const& path)
+{
+ if (this->Components.empty()) {
+ return path;
+ }
+
+ std::string::size_type pos = path.find('/');
+
+ return path.substr(pos + 1);
+}
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h
new file mode 100644
index 0000000..fc0994c
--- /dev/null
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.h
@@ -0,0 +1,159 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmCPackWIXGenerator_h
+#define cmCPackWIXGenerator_h
+
+#include <CPack/cmCPackGenerator.h>
+
+#include "cmWIXPatch.h"
+#include "cmWIXShortcut.h"
+
+#include <map>
+#include <string>
+
+class cmWIXSourceWriter;
+class cmWIXDirectoriesSourceWriter;
+class cmWIXFilesSourceWriter;
+class cmWIXFeaturesSourceWriter;
+
+/** \class cmCPackWIXGenerator
+ * \brief A generator for WIX files
+ */
+class cmCPackWIXGenerator : public cmCPackGenerator
+{
+public:
+ cmCPackTypeMacro(cmCPackWIXGenerator, cmCPackGenerator);
+
+ cmCPackWIXGenerator();
+ ~cmCPackWIXGenerator();
+
+protected:
+ virtual int InitializeInternal();
+
+ virtual int PackageFiles();
+
+ virtual const char* GetOutputExtension() { return ".msi"; }
+
+ virtual enum CPackSetDestdirSupport SupportsSetDestdir() const
+ {
+ return SETDESTDIR_UNSUPPORTED;
+ }
+
+ virtual bool SupportsAbsoluteDestination() const { return false; }
+
+ virtual bool SupportsComponentInstallation() const { return true; }
+
+private:
+ typedef std::map<std::string, std::string> id_map_t;
+ typedef std::map<std::string, size_t> ambiguity_map_t;
+ typedef std::set<std::string> extension_set_t;
+
+ bool InitializeWiXConfiguration();
+
+ bool PackageFilesImpl();
+
+ void CreateWiXVariablesIncludeFile();
+
+ void CreateWiXPropertiesIncludeFile();
+
+ void CreateWiXProductFragmentIncludeFile();
+
+ void CopyDefinition(cmWIXSourceWriter& source, std::string const& name);
+
+ void AddDefinition(cmWIXSourceWriter& source, std::string const& name,
+ std::string const& value);
+
+ bool CreateWiXSourceFiles();
+
+ std::string GetProgramFilesFolderId() const;
+
+ bool GenerateMainSourceFileFromTemplate();
+
+ bool CreateFeatureHierarchy(cmWIXFeaturesSourceWriter& featureDefinitions);
+
+ bool AddComponentsToFeature(
+ std::string const& rootPath, std::string const& featureId,
+ cmWIXDirectoriesSourceWriter& directoryDefinitions,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions, cmWIXShortcuts& shortcuts);
+
+ bool CreateShortcuts(std::string const& cpackComponentName,
+ std::string const& featureId,
+ cmWIXShortcuts const& shortcuts,
+ bool emitUninstallShortcut,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions);
+
+ bool CreateShortcutsOfSpecificType(
+ cmWIXShortcuts::Type type, std::string const& cpackComponentName,
+ std::string const& featureId, std::string const& idPrefix,
+ cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions);
+
+ void AppendUserSuppliedExtraSources();
+
+ void AppendUserSuppliedExtraObjects(std::ostream& stream);
+
+ bool CreateLicenseFile();
+
+ bool RunWiXCommand(std::string const& command);
+
+ bool RunCandleCommand(std::string const& sourceFile,
+ std::string const& objectFile);
+
+ bool RunLightCommand(std::string const& objectFiles);
+
+ void AddDirectoryAndFileDefinitons(
+ std::string const& topdir, std::string const& directoryId,
+ cmWIXDirectoriesSourceWriter& directoryDefinitions,
+ cmWIXFilesSourceWriter& fileDefinitions,
+ cmWIXFeaturesSourceWriter& featureDefinitions,
+ std::vector<std::string> const& packageExecutables,
+ std::vector<std::string> const& desktopExecutables,
+ cmWIXShortcuts& shortcuts);
+
+ bool RequireOption(std::string const& name, std::string& value) const;
+
+ std::string GetArchitecture() const;
+
+ static std::string GenerateGUID();
+
+ static std::string QuotePath(std::string const& path);
+
+ static std::string GetRightmostExtension(std::string const& filename);
+
+ std::string PathToId(std::string const& path);
+
+ std::string CreateNewIdForPath(std::string const& path);
+
+ static std::string CreateHashedId(std::string const& path,
+ std::string const& normalizedFilename);
+
+ std::string NormalizeComponentForId(std::string const& component,
+ size_t& replacementCount);
+
+ static bool IsLegalIdCharacter(char c);
+
+ void CollectExtensions(std::string const& variableName,
+ extension_set_t& extensions);
+
+ void AddCustomFlags(std::string const& variableName, std::ostream& stream);
+
+ std::string RelativePathWithoutComponentPrefix(std::string const& path);
+
+ std::vector<std::string> WixSources;
+ id_map_t PathToIdMap;
+ ambiguity_map_t IdAmbiguityCounter;
+
+ extension_set_t CandleExtensions;
+ extension_set_t LightExtensions;
+
+ std::string CPackTopLevel;
+
+ cmWIXPatch* Patch;
+
+ cmWIXSourceWriter::GuidType ComponentGuidType;
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXAccessControlList.cxx b/Source/CPack/WiX/cmWIXAccessControlList.cxx
new file mode 100644
index 0000000..9f9b39c
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXAccessControlList.cxx
@@ -0,0 +1,126 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXAccessControlList.h"
+
+#include <CPack/cmCPackGenerator.h>
+
+#include <cmSystemTools.h>
+
+cmWIXAccessControlList::cmWIXAccessControlList(
+ cmCPackLog* logger, cmInstalledFile const& installedFile,
+ cmWIXSourceWriter& sourceWriter)
+ : Logger(logger)
+ , InstalledFile(installedFile)
+ , SourceWriter(sourceWriter)
+{
+}
+
+bool cmWIXAccessControlList::Apply()
+{
+ std::vector<std::string> entries;
+ this->InstalledFile.GetPropertyAsList("CPACK_WIX_ACL", entries);
+
+ for (size_t i = 0; i < entries.size(); ++i) {
+ this->CreatePermissionElement(entries[i]);
+ }
+
+ return true;
+}
+
+void cmWIXAccessControlList::CreatePermissionElement(std::string const& entry)
+{
+ std::string::size_type pos = entry.find('=');
+ if (pos == std::string::npos) {
+ this->ReportError(entry, "Did not find mandatory '='");
+ return;
+ }
+
+ std::string user_and_domain = entry.substr(0, pos);
+ std::string permission_string = entry.substr(pos + 1);
+
+ pos = user_and_domain.find('@');
+ std::string user;
+ std::string domain;
+ if (pos != std::string::npos) {
+ user = user_and_domain.substr(0, pos);
+ domain = user_and_domain.substr(pos + 1);
+ } else {
+ user = user_and_domain;
+ }
+
+ std::vector<std::string> permissions =
+ cmSystemTools::tokenize(permission_string, ",");
+
+ this->SourceWriter.BeginElement("Permission");
+ this->SourceWriter.AddAttribute("User", user);
+ if (!domain.empty()) {
+ this->SourceWriter.AddAttribute("Domain", domain);
+ }
+ for (size_t i = 0; i < permissions.size(); ++i) {
+ this->EmitBooleanAttribute(entry,
+ cmSystemTools::TrimWhitespace(permissions[i]));
+ }
+ this->SourceWriter.EndElement("Permission");
+}
+
+void cmWIXAccessControlList::ReportError(std::string const& entry,
+ std::string const& message)
+{
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Failed processing ACL entry '"
+ << entry << "': " << message << std::endl);
+}
+
+bool cmWIXAccessControlList::IsBooleanAttribute(std::string const& name)
+{
+ static const char* validAttributes[] = {
+ /* clang-format needs this comment to break after the opening brace */
+ "Append",
+ "ChangePermission",
+ "CreateChild",
+ "CreateFile",
+ "CreateLink",
+ "CreateSubkeys",
+ "Delete",
+ "DeleteChild",
+ "EnumerateSubkeys",
+ "Execute",
+ "FileAllRights",
+ "GenericAll",
+ "GenericExecute",
+ "GenericRead",
+ "GenericWrite",
+ "Notify",
+ "Read",
+ "ReadAttributes",
+ "ReadExtendedAttributes",
+ "ReadPermission",
+ "SpecificRightsAll",
+ "Synchronize",
+ "TakeOwnership",
+ "Traverse",
+ "Write",
+ "WriteAttributes",
+ "WriteExtendedAttributes",
+ 0
+ };
+
+ size_t i = 0;
+ while (validAttributes[i]) {
+ if (name == validAttributes[i++])
+ return true;
+ }
+
+ return false;
+}
+
+void cmWIXAccessControlList::EmitBooleanAttribute(std::string const& entry,
+ std::string const& name)
+{
+ if (!this->IsBooleanAttribute(name)) {
+ std::ostringstream message;
+ message << "Unknown boolean attribute '" << name << "'";
+ this->ReportError(entry, message.str());
+ }
+
+ this->SourceWriter.AddAttribute(name, "yes");
+}
diff --git a/Source/CPack/WiX/cmWIXAccessControlList.h b/Source/CPack/WiX/cmWIXAccessControlList.h
new file mode 100644
index 0000000..0a25ee5
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXAccessControlList.h
@@ -0,0 +1,34 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXAccessControlList_h
+#define cmWIXAccessControlList_h
+
+#include "cmWIXSourceWriter.h"
+
+#include <CPack/cmCPackLog.h>
+#include <cmInstalledFile.h>
+
+class cmWIXAccessControlList
+{
+public:
+ cmWIXAccessControlList(cmCPackLog* logger,
+ cmInstalledFile const& installedFile,
+ cmWIXSourceWriter& sourceWriter);
+
+ bool Apply();
+
+private:
+ void CreatePermissionElement(std::string const& entry);
+
+ void ReportError(std::string const& entry, std::string const& message);
+
+ bool IsBooleanAttribute(std::string const& name);
+
+ void EmitBooleanAttribute(std::string const& entry, std::string const& name);
+
+ cmCPackLog* Logger;
+ cmInstalledFile const& InstalledFile;
+ cmWIXSourceWriter& SourceWriter;
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
new file mode 100644
index 0000000..975dffb
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
@@ -0,0 +1,82 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXDirectoriesSourceWriter.h"
+
+cmWIXDirectoriesSourceWriter::cmWIXDirectoriesSourceWriter(
+ cmCPackLog* logger, std::string const& filename, GuidType componentGuidType)
+ : cmWIXSourceWriter(logger, filename, componentGuidType)
+{
+}
+
+void cmWIXDirectoriesSourceWriter::EmitStartMenuFolder(
+ std::string const& startMenuFolder)
+{
+ BeginElement("Directory");
+ AddAttribute("Id", "ProgramMenuFolder");
+
+ BeginElement("Directory");
+ AddAttribute("Id", "PROGRAM_MENU_FOLDER");
+ AddAttribute("Name", startMenuFolder);
+ EndElement("Directory");
+
+ EndElement("Directory");
+}
+
+void cmWIXDirectoriesSourceWriter::EmitDesktopFolder()
+{
+ BeginElement("Directory");
+ AddAttribute("Id", "DesktopFolder");
+ AddAttribute("Name", "Desktop");
+ EndElement("Directory");
+}
+
+void cmWIXDirectoriesSourceWriter::EmitStartupFolder()
+{
+ BeginElement("Directory");
+ AddAttribute("Id", "StartupFolder");
+ AddAttribute("Name", "Startup");
+ EndElement("Directory");
+}
+
+size_t cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory(
+ std::string const& programFilesFolderId,
+ std::string const& installRootString)
+{
+ size_t offset = 1;
+ if (!programFilesFolderId.empty()) {
+ BeginElement("Directory");
+ AddAttribute("Id", programFilesFolderId);
+ offset = 0;
+ }
+
+ std::vector<std::string> installRoot;
+
+ cmSystemTools::SplitPath(installRootString.c_str(), installRoot);
+
+ if (!installRoot.empty() && installRoot.back().empty()) {
+ installRoot.pop_back();
+ }
+
+ for (size_t i = 1; i < installRoot.size(); ++i) {
+ BeginElement("Directory");
+
+ if (i == installRoot.size() - 1) {
+ AddAttribute("Id", "INSTALL_ROOT");
+ } else {
+ std::ostringstream tmp;
+ tmp << "INSTALL_PREFIX_" << i;
+ AddAttribute("Id", tmp.str());
+ }
+
+ AddAttribute("Name", installRoot[i]);
+ }
+
+ return installRoot.size() - offset;
+}
+
+void cmWIXDirectoriesSourceWriter::EndInstallationPrefixDirectory(size_t size)
+{
+ for (size_t i = 0; i < size; ++i) {
+ EndElement("Directory");
+ }
+}
diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h
new file mode 100644
index 0000000..062efe3
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h
@@ -0,0 +1,34 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXDirectoriesSourceWriter_h
+#define cmWIXDirectoriesSourceWriter_h
+
+#include "cmWIXSourceWriter.h"
+
+#include <CPack/cmCPackGenerator.h>
+
+#include <string>
+
+/** \class cmWIXDirectoriesSourceWriter
+ * \brief Helper class to generate directories.wxs
+ */
+class cmWIXDirectoriesSourceWriter : public cmWIXSourceWriter
+{
+public:
+ cmWIXDirectoriesSourceWriter(cmCPackLog* logger, std::string const& filename,
+ GuidType componentGuidType);
+
+ void EmitStartMenuFolder(std::string const& startMenuFolder);
+
+ void EmitDesktopFolder();
+
+ void EmitStartupFolder();
+
+ size_t BeginInstallationPrefixDirectory(
+ std::string const& programFilesFolderId,
+ std::string const& installRootString);
+
+ void EndInstallationPrefixDirectory(size_t size);
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx
new file mode 100644
index 0000000..79a9fdd
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx
@@ -0,0 +1,95 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXFeaturesSourceWriter.h"
+
+cmWIXFeaturesSourceWriter::cmWIXFeaturesSourceWriter(
+ cmCPackLog* logger, std::string const& filename, GuidType componentGuidType)
+ : cmWIXSourceWriter(logger, filename, componentGuidType)
+{
+}
+
+void cmWIXFeaturesSourceWriter::CreateCMakePackageRegistryEntry(
+ std::string const& package, std::string const& upgradeGuid)
+{
+ BeginElement("Component");
+ AddAttribute("Id", "CM_PACKAGE_REGISTRY");
+ AddAttribute("Directory", "TARGETDIR");
+ AddAttribute("Guid", CreateGuidFromComponentId("CM_PACKAGE_REGISTRY"));
+
+ std::string registryKey =
+ std::string("Software\\Kitware\\CMake\\Packages\\") + package;
+
+ BeginElement("RegistryValue");
+ AddAttribute("Root", "HKLM");
+ AddAttribute("Key", registryKey);
+ AddAttribute("Name", upgradeGuid);
+ AddAttribute("Type", "string");
+ AddAttribute("Value", "[INSTALL_ROOT]");
+ AddAttribute("KeyPath", "yes");
+ EndElement("RegistryValue");
+
+ EndElement("Component");
+}
+
+void cmWIXFeaturesSourceWriter::EmitFeatureForComponentGroup(
+ cmCPackComponentGroup const& group, cmWIXPatch& patch)
+{
+ BeginElement("Feature");
+ AddAttribute("Id", "CM_G_" + group.Name);
+
+ if (group.IsExpandedByDefault) {
+ AddAttribute("Display", "expand");
+ }
+
+ AddAttributeUnlessEmpty("Title", group.DisplayName);
+ AddAttributeUnlessEmpty("Description", group.Description);
+
+ for (std::vector<cmCPackComponentGroup*>::const_iterator i =
+ group.Subgroups.begin();
+ i != group.Subgroups.end(); ++i) {
+ EmitFeatureForComponentGroup(**i, patch);
+ }
+
+ for (std::vector<cmCPackComponent*>::const_iterator i =
+ group.Components.begin();
+ i != group.Components.end(); ++i) {
+ EmitFeatureForComponent(**i, patch);
+ }
+
+ patch.ApplyFragment("CM_G_" + group.Name, *this);
+
+ EndElement("Feature");
+}
+
+void cmWIXFeaturesSourceWriter::EmitFeatureForComponent(
+ cmCPackComponent const& component, cmWIXPatch& patch)
+{
+ BeginElement("Feature");
+ AddAttribute("Id", "CM_C_" + component.Name);
+
+ AddAttributeUnlessEmpty("Title", component.DisplayName);
+ AddAttributeUnlessEmpty("Description", component.Description);
+
+ if (component.IsRequired) {
+ AddAttribute("Absent", "disallow");
+ }
+
+ if (component.IsHidden) {
+ AddAttribute("Display", "hidden");
+ }
+
+ if (component.IsDisabledByDefault) {
+ AddAttribute("Level", "2");
+ }
+
+ patch.ApplyFragment("CM_C_" + component.Name, *this);
+
+ EndElement("Feature");
+}
+
+void cmWIXFeaturesSourceWriter::EmitComponentRef(std::string const& id)
+{
+ BeginElement("ComponentRef");
+ AddAttribute("Id", id);
+ EndElement("ComponentRef");
+}
diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h
new file mode 100644
index 0000000..368b250
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h
@@ -0,0 +1,32 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXFeaturesSourceWriter_h
+#define cmWIXFeaturesSourceWriter_h
+
+#include "cmWIXPatch.h"
+#include "cmWIXSourceWriter.h"
+
+#include <CPack/cmCPackGenerator.h>
+
+/** \class cmWIXFeaturesSourceWriter
+ * \brief Helper class to generate features.wxs
+ */
+class cmWIXFeaturesSourceWriter : public cmWIXSourceWriter
+{
+public:
+ cmWIXFeaturesSourceWriter(cmCPackLog* logger, std::string const& filename,
+ GuidType componentGuidType);
+
+ void CreateCMakePackageRegistryEntry(std::string const& package,
+ std::string const& upgradeGuid);
+
+ void EmitFeatureForComponentGroup(const cmCPackComponentGroup& group,
+ cmWIXPatch& patch);
+
+ void EmitFeatureForComponent(const cmCPackComponent& component,
+ cmWIXPatch& patch);
+
+ void EmitComponentRef(std::string const& id);
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
new file mode 100644
index 0000000..a471d26
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
@@ -0,0 +1,166 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXFilesSourceWriter.h"
+
+#include "cmWIXAccessControlList.h"
+
+#include <cmInstalledFile.h>
+
+#include <cmSystemTools.h>
+#include <cmUuid.h>
+
+#include <sys/types.h>
+// include sys/stat.h after sys/types.h
+#include <sys/stat.h>
+
+cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(cmCPackLog* logger,
+ std::string const& filename,
+ GuidType componentGuidType)
+ : cmWIXSourceWriter(logger, filename, componentGuidType)
+{
+}
+
+void cmWIXFilesSourceWriter::EmitShortcut(std::string const& id,
+ cmWIXShortcut const& shortcut,
+ std::string const& shortcutPrefix,
+ size_t shortcutIndex)
+{
+ std::ostringstream shortcutId;
+ shortcutId << shortcutPrefix << id;
+
+ if (shortcutIndex > 0) {
+ shortcutId << "_" << shortcutIndex;
+ }
+
+ std::string fileId = std::string("CM_F") + id;
+
+ BeginElement("Shortcut");
+ AddAttribute("Id", shortcutId.str());
+ AddAttribute("Name", shortcut.label);
+ std::string target = "[#" + fileId + "]";
+ AddAttribute("Target", target);
+ AddAttribute("WorkingDirectory", shortcut.workingDirectoryId);
+ EndElement("Shortcut");
+}
+
+void cmWIXFilesSourceWriter::EmitRemoveFolder(std::string const& id)
+{
+ BeginElement("RemoveFolder");
+ AddAttribute("Id", id);
+ AddAttribute("On", "uninstall");
+ EndElement("RemoveFolder");
+}
+
+void cmWIXFilesSourceWriter::EmitInstallRegistryValue(
+ std::string const& registryKey, std::string const& cpackComponentName,
+ std::string const& suffix)
+{
+ std::string valueName;
+ if (!cpackComponentName.empty()) {
+ valueName = cpackComponentName + "_";
+ }
+
+ valueName += "installed";
+ valueName += suffix;
+
+ BeginElement("RegistryValue");
+ AddAttribute("Root", "HKCU");
+ AddAttribute("Key", registryKey);
+ AddAttribute("Name", valueName);
+ AddAttribute("Type", "integer");
+ AddAttribute("Value", "1");
+ AddAttribute("KeyPath", "yes");
+ EndElement("RegistryValue");
+}
+
+void cmWIXFilesSourceWriter::EmitUninstallShortcut(
+ std::string const& packageName)
+{
+ BeginElement("Shortcut");
+ AddAttribute("Id", "UNINSTALL");
+ AddAttribute("Name", "Uninstall " + packageName);
+ AddAttribute("Description", "Uninstalls " + packageName);
+ AddAttribute("Target", "[SystemFolder]msiexec.exe");
+ AddAttribute("Arguments", "/x [ProductCode]");
+ EndElement("Shortcut");
+}
+
+std::string cmWIXFilesSourceWriter::EmitComponentCreateFolder(
+ std::string const& directoryId, std::string const& guid,
+ cmInstalledFile const* installedFile)
+{
+ std::string componentId = std::string("CM_C_EMPTY_") + directoryId;
+
+ BeginElement("DirectoryRef");
+ AddAttribute("Id", directoryId);
+
+ BeginElement("Component");
+ AddAttribute("Id", componentId);
+ AddAttribute("Guid", guid);
+
+ BeginElement("CreateFolder");
+
+ if (installedFile) {
+ cmWIXAccessControlList acl(Logger, *installedFile, *this);
+ acl.Apply();
+ }
+
+ EndElement("CreateFolder");
+ EndElement("Component");
+ EndElement("DirectoryRef");
+
+ return componentId;
+}
+
+std::string cmWIXFilesSourceWriter::EmitComponentFile(
+ std::string const& directoryId, std::string const& id,
+ std::string const& filePath, cmWIXPatch& patch,
+ cmInstalledFile const* installedFile)
+{
+ std::string componentId = std::string("CM_C") + id;
+ std::string fileId = std::string("CM_F") + id;
+
+ std::string guid = CreateGuidFromComponentId(componentId);
+
+ BeginElement("DirectoryRef");
+ AddAttribute("Id", directoryId);
+
+ BeginElement("Component");
+ AddAttribute("Id", componentId);
+ AddAttribute("Guid", guid);
+
+ if (installedFile) {
+ if (installedFile->GetPropertyAsBool("CPACK_NEVER_OVERWRITE")) {
+ AddAttribute("NeverOverwrite", "yes");
+ }
+ if (installedFile->GetPropertyAsBool("CPACK_PERMANENT")) {
+ AddAttribute("Permanent", "yes");
+ }
+ }
+
+ BeginElement("File");
+ AddAttribute("Id", fileId);
+ AddAttribute("Source", filePath);
+ AddAttribute("KeyPath", "yes");
+
+ mode_t fileMode = 0;
+ cmSystemTools::GetPermissions(filePath.c_str(), fileMode);
+
+ if (!(fileMode & S_IWRITE)) {
+ AddAttribute("ReadOnly", "yes");
+ }
+
+ if (installedFile) {
+ cmWIXAccessControlList acl(Logger, *installedFile, *this);
+ acl.Apply();
+ }
+
+ patch.ApplyFragment(fileId, *this);
+ EndElement("File");
+
+ patch.ApplyFragment(componentId, *this);
+ EndElement("Component");
+ EndElement("DirectoryRef");
+
+ return componentId;
+}
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.h b/Source/CPack/WiX/cmWIXFilesSourceWriter.h
new file mode 100644
index 0000000..d7a642d
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.h
@@ -0,0 +1,43 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXFilesSourceWriter_h
+#define cmWIXFilesSourceWriter_h
+
+#include "cmWIXSourceWriter.h"
+
+#include "cmWIXPatch.h"
+#include "cmWIXShortcut.h"
+
+#include <CPack/cmCPackGenerator.h>
+
+/** \class cmWIXFilesSourceWriter
+ * \brief Helper class to generate files.wxs
+ */
+class cmWIXFilesSourceWriter : public cmWIXSourceWriter
+{
+public:
+ cmWIXFilesSourceWriter(cmCPackLog* logger, std::string const& filename,
+ GuidType componentGuidType);
+
+ void EmitShortcut(std::string const& id, cmWIXShortcut const& shortcut,
+ std::string const& shortcutPrefix, size_t shortcutIndex);
+
+ void EmitRemoveFolder(std::string const& id);
+
+ void EmitInstallRegistryValue(std::string const& registryKey,
+ std::string const& cpackComponentName,
+ std::string const& suffix);
+
+ void EmitUninstallShortcut(std::string const& packageName);
+
+ std::string EmitComponentCreateFolder(std::string const& directoryId,
+ std::string const& guid,
+ cmInstalledFile const* installedFile);
+
+ std::string EmitComponentFile(std::string const& directoryId,
+ std::string const& id,
+ std::string const& filePath, cmWIXPatch& patch,
+ cmInstalledFile const* installedFile);
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXPatch.cxx b/Source/CPack/WiX/cmWIXPatch.cxx
new file mode 100644
index 0000000..7c48653
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXPatch.cxx
@@ -0,0 +1,95 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXPatch.h"
+
+#include <CPack/cmCPackGenerator.h>
+
+cmWIXPatch::cmWIXPatch(cmCPackLog* logger)
+ : Logger(logger)
+{
+}
+
+bool cmWIXPatch::LoadFragments(std::string const& patchFilePath)
+{
+ cmWIXPatchParser parser(Fragments, Logger);
+ if (!parser.ParseFile(patchFilePath.c_str())) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Failed parsing XML patch file: '"
+ << patchFilePath << "'" << std::endl);
+ return false;
+ }
+
+ return true;
+}
+
+void cmWIXPatch::ApplyFragment(std::string const& id,
+ cmWIXSourceWriter& writer)
+{
+ cmWIXPatchParser::fragment_map_t::iterator i = Fragments.find(id);
+ if (i == Fragments.end())
+ return;
+
+ const cmWIXPatchElement& fragment = i->second;
+
+ this->ApplyElementChildren(fragment, writer);
+
+ Fragments.erase(i);
+}
+
+void cmWIXPatch::ApplyElementChildren(const cmWIXPatchElement& element,
+ cmWIXSourceWriter& writer)
+{
+ for (cmWIXPatchElement::child_list_t::const_iterator j =
+ element.children.begin();
+ j != element.children.end(); ++j) {
+ cmWIXPatchNode* node = *j;
+
+ switch (node->type()) {
+ case cmWIXPatchNode::ELEMENT:
+ ApplyElement(dynamic_cast<const cmWIXPatchElement&>(*node), writer);
+ break;
+ case cmWIXPatchNode::TEXT:
+ writer.AddTextNode(dynamic_cast<const cmWIXPatchText&>(*node).text);
+ break;
+ }
+ }
+}
+
+void cmWIXPatch::ApplyElement(const cmWIXPatchElement& element,
+ cmWIXSourceWriter& writer)
+{
+ writer.BeginElement(element.name);
+
+ for (cmWIXPatchElement::attributes_t::const_iterator i =
+ element.attributes.begin();
+ i != element.attributes.end(); ++i) {
+ writer.AddAttribute(i->first, i->second);
+ }
+
+ this->ApplyElementChildren(element, writer);
+
+ writer.EndElement(element.name);
+}
+
+bool cmWIXPatch::CheckForUnappliedFragments()
+{
+ std::string fragmentList;
+ for (cmWIXPatchParser::fragment_map_t::const_iterator i = Fragments.begin();
+ i != Fragments.end(); ++i) {
+ if (!fragmentList.empty()) {
+ fragmentList += ", ";
+ }
+
+ fragmentList += "'";
+ fragmentList += i->first;
+ fragmentList += "'";
+ }
+
+ if (!fragmentList.empty()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Some XML patch fragments did not have matching IDs: "
+ << fragmentList << std::endl);
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/CPack/WiX/cmWIXPatch.h b/Source/CPack/WiX/cmWIXPatch.h
new file mode 100644
index 0000000..a4c9e71
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXPatch.h
@@ -0,0 +1,37 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXPatch_h
+#define cmWIXPatch_h
+
+#include "cmWIXPatchParser.h"
+#include "cmWIXSourceWriter.h"
+
+#include <string>
+
+/** \class cmWIXPatch
+ * \brief Class that maintains and applies patch fragments
+ */
+class cmWIXPatch
+{
+public:
+ cmWIXPatch(cmCPackLog* logger);
+
+ bool LoadFragments(std::string const& patchFilePath);
+
+ void ApplyFragment(std::string const& id, cmWIXSourceWriter& writer);
+
+ bool CheckForUnappliedFragments();
+
+private:
+ void ApplyElementChildren(const cmWIXPatchElement& element,
+ cmWIXSourceWriter& writer);
+
+ void ApplyElement(const cmWIXPatchElement& element,
+ cmWIXSourceWriter& writer);
+
+ cmCPackLog* Logger;
+
+ cmWIXPatchParser::fragment_map_t Fragments;
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXPatchParser.cxx b/Source/CPack/WiX/cmWIXPatchParser.cxx
new file mode 100644
index 0000000..47f98d1
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXPatchParser.cxx
@@ -0,0 +1,146 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXPatchParser.h"
+
+#include <CPack/cmCPackGenerator.h>
+
+#include <cm_expat.h>
+
+cmWIXPatchNode::Type cmWIXPatchText::type()
+{
+ return cmWIXPatchNode::TEXT;
+}
+
+cmWIXPatchNode::Type cmWIXPatchElement::type()
+{
+ return cmWIXPatchNode::ELEMENT;
+}
+
+cmWIXPatchNode::~cmWIXPatchNode()
+{
+}
+
+cmWIXPatchElement::~cmWIXPatchElement()
+{
+ for (child_list_t::iterator i = children.begin(); i != children.end(); ++i) {
+ delete *i;
+ }
+}
+
+cmWIXPatchParser::cmWIXPatchParser(fragment_map_t& fragments,
+ cmCPackLog* logger)
+ : Logger(logger)
+ , State(BEGIN_DOCUMENT)
+ , Valid(true)
+ , Fragments(fragments)
+{
+}
+
+void cmWIXPatchParser::StartElement(const std::string& name, const char** atts)
+{
+ if (State == BEGIN_DOCUMENT) {
+ if (name == "CPackWiXPatch") {
+ State = BEGIN_FRAGMENTS;
+ } else {
+ ReportValidationError("Expected root element 'CPackWiXPatch'");
+ }
+ } else if (State == BEGIN_FRAGMENTS) {
+ if (name == "CPackWiXFragment") {
+ State = INSIDE_FRAGMENT;
+ StartFragment(atts);
+ } else {
+ ReportValidationError("Expected 'CPackWixFragment' element");
+ }
+ } else if (State == INSIDE_FRAGMENT) {
+ cmWIXPatchElement& parent = *ElementStack.back();
+
+ cmWIXPatchElement* element = new cmWIXPatchElement;
+ parent.children.push_back(element);
+
+ element->name = name;
+
+ for (size_t i = 0; atts[i]; i += 2) {
+ std::string key = atts[i];
+ std::string value = atts[i + 1];
+
+ element->attributes[key] = value;
+ }
+
+ ElementStack.push_back(element);
+ }
+}
+
+void cmWIXPatchParser::StartFragment(const char** attributes)
+{
+ for (size_t i = 0; attributes[i]; i += 2) {
+ std::string key = attributes[i];
+ std::string value = attributes[i + 1];
+
+ if (key == "Id") {
+ if (Fragments.find(value) != Fragments.end()) {
+ std::ostringstream tmp;
+ tmp << "Invalid reuse of 'CPackWixFragment' 'Id': " << value;
+ ReportValidationError(tmp.str());
+ }
+
+ ElementStack.push_back(&Fragments[value]);
+ } else {
+ ReportValidationError(
+ "The only allowed 'CPackWixFragment' attribute is 'Id'");
+ }
+ }
+}
+
+void cmWIXPatchParser::EndElement(const std::string& name)
+{
+ if (State == INSIDE_FRAGMENT) {
+ if (name == "CPackWiXFragment") {
+ State = BEGIN_FRAGMENTS;
+ ElementStack.clear();
+ } else {
+ ElementStack.pop_back();
+ }
+ }
+}
+
+void cmWIXPatchParser::CharacterDataHandler(const char* data, int length)
+{
+ const char* whitespace = "\x20\x09\x0d\x0a";
+
+ if (State == INSIDE_FRAGMENT) {
+ cmWIXPatchElement& parent = *ElementStack.back();
+
+ std::string text(data, length);
+
+ std::string::size_type first = text.find_first_not_of(whitespace);
+ std::string::size_type last = text.find_last_not_of(whitespace);
+
+ if (first != std::string::npos && last != std::string::npos) {
+ cmWIXPatchText* text_node = new cmWIXPatchText;
+ text_node->text = text.substr(first, last - first + 1);
+
+ parent.children.push_back(text_node);
+ }
+ }
+}
+
+void cmWIXPatchParser::ReportError(int line, int column, const char* msg)
+{
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Error while processing XML patch file at "
+ << line << ":" << column << ": " << msg << std::endl);
+ Valid = false;
+}
+
+void cmWIXPatchParser::ReportValidationError(std::string const& message)
+{
+ ReportError(
+ XML_GetCurrentLineNumber(static_cast<XML_Parser>(this->Parser)),
+ XML_GetCurrentColumnNumber(static_cast<XML_Parser>(this->Parser)),
+ message.c_str());
+}
+
+bool cmWIXPatchParser::IsValid() const
+{
+ return Valid;
+}
diff --git a/Source/CPack/WiX/cmWIXPatchParser.h b/Source/CPack/WiX/cmWIXPatchParser.h
new file mode 100644
index 0000000..a2f0a3c
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXPatchParser.h
@@ -0,0 +1,90 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmCPackWIXPatchParser_h
+#define cmCPackWIXPatchParser_h
+
+#include <CPack/cmCPackLog.h>
+
+#include <cmXMLParser.h>
+
+#include <list>
+#include <map>
+
+struct cmWIXPatchNode
+{
+ enum Type
+ {
+ TEXT,
+ ELEMENT
+ };
+
+ virtual ~cmWIXPatchNode();
+
+ virtual Type type() = 0;
+};
+
+struct cmWIXPatchText : public cmWIXPatchNode
+{
+ virtual Type type();
+
+ std::string text;
+};
+
+struct cmWIXPatchElement : cmWIXPatchNode
+{
+ virtual Type type();
+
+ ~cmWIXPatchElement();
+
+ typedef std::list<cmWIXPatchNode*> child_list_t;
+ typedef std::map<std::string, std::string> attributes_t;
+
+ std::string name;
+ child_list_t children;
+ attributes_t attributes;
+};
+
+/** \class cmWIXPatchParser
+ * \brief Helper class that parses XML patch files (CPACK_WIX_PATCH_FILE)
+ */
+class cmWIXPatchParser : public cmXMLParser
+{
+public:
+ typedef std::map<std::string, cmWIXPatchElement> fragment_map_t;
+
+ cmWIXPatchParser(fragment_map_t& Fragments, cmCPackLog* logger);
+
+private:
+ virtual void StartElement(const std::string& name, const char** atts);
+
+ void StartFragment(const char** attributes);
+
+ virtual void EndElement(const std::string& name);
+
+ virtual void CharacterDataHandler(const char* data, int length);
+
+ virtual void ReportError(int line, int column, const char* msg);
+
+ void ReportValidationError(std::string const& message);
+
+ bool IsValid() const;
+
+ cmCPackLog* Logger;
+
+ enum ParserState
+ {
+ BEGIN_DOCUMENT,
+ BEGIN_FRAGMENTS,
+ INSIDE_FRAGMENT
+ };
+
+ ParserState State;
+
+ bool Valid;
+
+ fragment_map_t& Fragments;
+
+ std::list<cmWIXPatchElement*> ElementStack;
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
new file mode 100644
index 0000000..d819347
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
@@ -0,0 +1,186 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXRichTextFormatWriter.h"
+
+#include <cmVersion.h>
+
+cmWIXRichTextFormatWriter::cmWIXRichTextFormatWriter(
+ std::string const& filename)
+ : File(filename.c_str(), std::ios::binary)
+{
+ StartGroup();
+ WriteHeader();
+ WriteDocumentPrefix();
+}
+
+cmWIXRichTextFormatWriter::~cmWIXRichTextFormatWriter()
+{
+ EndGroup();
+
+ /* I haven't seen this in the RTF spec but
+ * wordpad terminates its RTF like this */
+ File << "\r\n";
+ File.put(0);
+}
+
+void cmWIXRichTextFormatWriter::AddText(std::string const& text)
+{
+ typedef unsigned char rtf_byte_t;
+
+ for (size_t i = 0; i < text.size(); ++i) {
+ rtf_byte_t c = rtf_byte_t(text[i]);
+
+ switch (c) {
+ case '\\':
+ File << "\\\\";
+ break;
+ case '{':
+ File << "\\{";
+ break;
+ case '}':
+ File << "\\}";
+ break;
+ case '\n':
+ File << "\\par\r\n";
+ break;
+ case '\r':
+ continue;
+ default: {
+ if (c <= 0x7F) {
+ File << c;
+ } else {
+ if (c <= 0xC0) {
+ EmitInvalidCodepoint(c);
+ } else if (c < 0xE0 && i + 1 < text.size()) {
+ EmitUnicodeCodepoint((text[i + 1] & 0x3F) | ((c & 0x1F) << 6));
+ i += 1;
+ } else if (c < 0xF0 && i + 2 < text.size()) {
+ EmitUnicodeCodepoint((text[i + 2] & 0x3F) |
+ ((text[i + 1] & 0x3F) << 6) |
+ ((c & 0xF) << 12));
+ i += 2;
+ } else if (c < 0xF8 && i + 3 < text.size()) {
+ EmitUnicodeCodepoint(
+ (text[i + 3] & 0x3F) | ((text[i + 2] & 0x3F) << 6) |
+ ((text[i + 1] & 0x3F) << 12) | ((c & 0x7) << 18));
+ i += 3;
+ } else {
+ EmitInvalidCodepoint(c);
+ }
+ }
+ } break;
+ }
+ }
+}
+
+void cmWIXRichTextFormatWriter::WriteHeader()
+{
+ ControlWord("rtf1");
+ ControlWord("ansi");
+ ControlWord("ansicpg1252");
+ ControlWord("deff0");
+ ControlWord("deflang1031");
+
+ WriteFontTable();
+ WriteColorTable();
+ WriteGenerator();
+}
+
+void cmWIXRichTextFormatWriter::WriteFontTable()
+{
+ StartGroup();
+ ControlWord("fonttbl");
+
+ StartGroup();
+ ControlWord("f0");
+ ControlWord("fswiss");
+ ControlWord("fcharset0 Arial;");
+ EndGroup();
+
+ EndGroup();
+}
+
+void cmWIXRichTextFormatWriter::WriteColorTable()
+{
+ StartGroup();
+ ControlWord("colortbl ;");
+ ControlWord("red255");
+ ControlWord("green0");
+ ControlWord("blue0;");
+ ControlWord("red0");
+ ControlWord("green255");
+ ControlWord("blue0;");
+ ControlWord("red0");
+ ControlWord("green0");
+ ControlWord("blue255;");
+ EndGroup();
+}
+
+void cmWIXRichTextFormatWriter::WriteGenerator()
+{
+ StartGroup();
+ NewControlWord("generator");
+ File << " CPack WiX Generator (" << cmVersion::GetCMakeVersion() << ");";
+ EndGroup();
+}
+
+void cmWIXRichTextFormatWriter::WriteDocumentPrefix()
+{
+ ControlWord("viewkind4");
+ ControlWord("uc1");
+ ControlWord("pard");
+ ControlWord("f0");
+ ControlWord("fs20");
+}
+
+void cmWIXRichTextFormatWriter::ControlWord(std::string const& keyword)
+{
+ File << "\\" << keyword;
+}
+
+void cmWIXRichTextFormatWriter::NewControlWord(std::string const& keyword)
+{
+ File << "\\*\\" << keyword;
+}
+
+void cmWIXRichTextFormatWriter::StartGroup()
+{
+ File.put('{');
+}
+
+void cmWIXRichTextFormatWriter::EndGroup()
+{
+ File.put('}');
+}
+
+void cmWIXRichTextFormatWriter::EmitUnicodeCodepoint(int c)
+{
+ // Do not emit byte order mark (BOM)
+ if (c == 0xFEFF) {
+ return;
+ } else if (c <= 0xFFFF) {
+ EmitUnicodeSurrogate(c);
+ } else {
+ c -= 0x10000;
+ EmitUnicodeSurrogate(((c >> 10) & 0x3FF) + 0xD800);
+ EmitUnicodeSurrogate((c & 0x3FF) + 0xDC00);
+ }
+}
+
+void cmWIXRichTextFormatWriter::EmitUnicodeSurrogate(int c)
+{
+ ControlWord("u");
+ if (c <= 32767) {
+ File << c;
+ } else {
+ File << (c - 65536);
+ }
+ File << "?";
+}
+
+void cmWIXRichTextFormatWriter::EmitInvalidCodepoint(int c)
+{
+ ControlWord("cf1 ");
+ File << "[INVALID-BYTE-" << int(c) << "]";
+ ControlWord("cf0 ");
+}
diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.h b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h
new file mode 100644
index 0000000..a3c8394
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h
@@ -0,0 +1,45 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXRichTextFormatWriter_h
+#define cmWIXRichTextFormatWriter_h
+
+#include <cmConfigure.h>
+
+#include <cmsys/FStream.hxx>
+#include <string>
+
+/** \class cmWIXRichtTextFormatWriter
+ * \brief Helper class to generate Rich Text Format (RTF) documents
+ * from plain text (e.g. for license and welcome text)
+ */
+class cmWIXRichTextFormatWriter
+{
+public:
+ cmWIXRichTextFormatWriter(std::string const& filename);
+ ~cmWIXRichTextFormatWriter();
+
+ void AddText(std::string const& text);
+
+private:
+ void WriteHeader();
+ void WriteFontTable();
+ void WriteColorTable();
+ void WriteGenerator();
+
+ void WriteDocumentPrefix();
+
+ void ControlWord(std::string const& keyword);
+ void NewControlWord(std::string const& keyword);
+
+ void StartGroup();
+ void EndGroup();
+
+ void EmitUnicodeCodepoint(int c);
+ void EmitUnicodeSurrogate(int c);
+
+ void EmitInvalidCodepoint(int c);
+
+ cmsys::ofstream File;
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXShortcut.cxx b/Source/CPack/WiX/cmWIXShortcut.cxx
new file mode 100644
index 0000000..e5dea94
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXShortcut.cxx
@@ -0,0 +1,105 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXShortcut.h"
+
+#include "cmWIXFilesSourceWriter.h"
+
+void cmWIXShortcuts::insert(Type type, std::string const& id,
+ cmWIXShortcut const& shortcut)
+{
+ this->Shortcuts[type][id].push_back(shortcut);
+}
+
+bool cmWIXShortcuts::empty(Type type) const
+{
+ return this->Shortcuts.find(type) == this->Shortcuts.end();
+}
+
+bool cmWIXShortcuts::EmitShortcuts(
+ Type type, std::string const& registryKey,
+ std::string const& cpackComponentName,
+ cmWIXFilesSourceWriter& fileDefinitions) const
+{
+ shortcut_type_map_t::const_iterator i = this->Shortcuts.find(type);
+
+ if (i == this->Shortcuts.end()) {
+ return false;
+ }
+
+ shortcut_id_map_t const& id_map = i->second;
+
+ std::string shortcutPrefix;
+ std::string registrySuffix;
+
+ switch (type) {
+ case START_MENU:
+ shortcutPrefix = "CM_S";
+ break;
+ case DESKTOP:
+ shortcutPrefix = "CM_DS";
+ registrySuffix = "_desktop";
+ break;
+ case STARTUP:
+ shortcutPrefix = "CM_SS";
+ registrySuffix = "_startup";
+ break;
+ default:
+ return false;
+ }
+
+ for (shortcut_id_map_t::const_iterator j = id_map.begin(); j != id_map.end();
+ ++j) {
+ std::string const& id = j->first;
+ shortcut_list_t const& shortcutList = j->second;
+
+ for (size_t shortcutListIndex = 0; shortcutListIndex < shortcutList.size();
+ ++shortcutListIndex) {
+ cmWIXShortcut const& shortcut = shortcutList[shortcutListIndex];
+ fileDefinitions.EmitShortcut(id, shortcut, shortcutPrefix,
+ shortcutListIndex);
+ }
+ }
+
+ fileDefinitions.EmitInstallRegistryValue(registryKey, cpackComponentName,
+ registrySuffix);
+
+ return true;
+}
+
+void cmWIXShortcuts::AddShortcutTypes(std::set<Type>& types)
+{
+ for (shortcut_type_map_t::const_iterator i = this->Shortcuts.begin();
+ i != this->Shortcuts.end(); ++i) {
+ types.insert(i->first);
+ }
+}
+
+void cmWIXShortcuts::CreateFromProperties(std::string const& id,
+ std::string const& directoryId,
+ cmInstalledFile const& installedFile)
+{
+ CreateFromProperty("CPACK_START_MENU_SHORTCUTS", START_MENU, id, directoryId,
+ installedFile);
+
+ CreateFromProperty("CPACK_DESKTOP_SHORTCUTS", DESKTOP, id, directoryId,
+ installedFile);
+
+ CreateFromProperty("CPACK_STARTUP_SHORTCUTS", STARTUP, id, directoryId,
+ installedFile);
+}
+
+void cmWIXShortcuts::CreateFromProperty(std::string const& propertyName,
+ Type type, std::string const& id,
+ std::string const& directoryId,
+ cmInstalledFile const& installedFile)
+{
+ std::vector<std::string> list;
+ installedFile.GetPropertyAsList(propertyName, list);
+
+ for (size_t i = 0; i < list.size(); ++i) {
+ cmWIXShortcut shortcut;
+ shortcut.label = list[i];
+ shortcut.workingDirectoryId = directoryId;
+ insert(type, id, shortcut);
+ }
+}
diff --git a/Source/CPack/WiX/cmWIXShortcut.h b/Source/CPack/WiX/cmWIXShortcut.h
new file mode 100644
index 0000000..cba3b34
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXShortcut.h
@@ -0,0 +1,60 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXShortcut_h
+#define cmWIXShortcut_h
+
+#include <cmInstalledFile.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+class cmWIXFilesSourceWriter;
+
+struct cmWIXShortcut
+{
+ std::string label;
+ std::string workingDirectoryId;
+};
+
+class cmWIXShortcuts
+{
+public:
+ enum Type
+ {
+ START_MENU,
+ DESKTOP,
+ STARTUP
+ };
+
+ typedef std::vector<cmWIXShortcut> shortcut_list_t;
+ typedef std::map<std::string, shortcut_list_t> shortcut_id_map_t;
+
+ void insert(Type type, std::string const& id, cmWIXShortcut const& shortcut);
+
+ bool empty(Type type) const;
+
+ bool EmitShortcuts(Type type, std::string const& registryKey,
+ std::string const& cpackComponentName,
+ cmWIXFilesSourceWriter& fileDefinitions) const;
+
+ void AddShortcutTypes(std::set<Type>& types);
+
+ void CreateFromProperties(std::string const& id,
+ std::string const& directoryId,
+ cmInstalledFile const& installedFile);
+
+private:
+ typedef std::map<Type, shortcut_id_map_t> shortcut_type_map_t;
+
+ void CreateFromProperty(std::string const& propertyName, Type type,
+ std::string const& id,
+ std::string const& directoryId,
+ cmInstalledFile const& installedFile);
+
+ shortcut_type_map_t Shortcuts;
+ shortcut_id_map_t EmptyIdMap;
+};
+
+#endif
diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx
new file mode 100644
index 0000000..b434334
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx
@@ -0,0 +1,184 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWIXSourceWriter.h"
+
+#include <CPack/cmCPackGenerator.h>
+
+#include <cmUuid.h>
+
+#include <windows.h>
+
+cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger,
+ std::string const& filename,
+ GuidType componentGuidType,
+ RootElementType rootElementType)
+ : Logger(logger)
+ , File(filename.c_str())
+ , State(DEFAULT)
+ , SourceFilename(filename)
+ , ComponentGuidType(componentGuidType)
+{
+ WriteXMLDeclaration();
+
+ if (rootElementType == INCLUDE_ELEMENT_ROOT) {
+ BeginElement("Include");
+ } else {
+ BeginElement("Wix");
+ }
+
+ AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi");
+}
+
+cmWIXSourceWriter::~cmWIXSourceWriter()
+{
+ if (Elements.size() > 1) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, Elements.size() - 1
+ << " WiX elements were still open when closing '"
+ << SourceFilename << "'" << std::endl);
+ return;
+ }
+
+ EndElement(Elements.back());
+}
+
+void cmWIXSourceWriter::BeginElement(std::string const& name)
+{
+ if (State == BEGIN) {
+ File << ">";
+ }
+
+ File << "\n";
+ Indent(Elements.size());
+ File << "<" << name;
+
+ Elements.push_back(name);
+ State = BEGIN;
+}
+
+void cmWIXSourceWriter::EndElement(std::string const& name)
+{
+ if (Elements.empty()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "can not end WiX element with no open elements in '"
+ << SourceFilename << "'" << std::endl);
+ return;
+ }
+
+ if (Elements.back() != name) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "WiX element <"
+ << Elements.back() << "> can not be closed by </" << name
+ << "> in '" << SourceFilename << "'" << std::endl);
+ return;
+ }
+
+ if (State == DEFAULT) {
+ File << "\n";
+ Indent(Elements.size() - 1);
+ File << "</" << Elements.back() << ">";
+ } else {
+ File << "/>";
+ }
+
+ Elements.pop_back();
+ State = DEFAULT;
+}
+
+void cmWIXSourceWriter::AddTextNode(std::string const& text)
+{
+ if (State == BEGIN) {
+ File << ">";
+ }
+
+ if (Elements.empty()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "can not add text without open WiX element in '"
+ << SourceFilename << "'" << std::endl);
+ return;
+ }
+
+ File << this->EscapeAttributeValue(text);
+ State = DEFAULT;
+}
+
+void cmWIXSourceWriter::AddProcessingInstruction(std::string const& target,
+ std::string const& content)
+{
+ if (State == BEGIN) {
+ File << ">";
+ }
+
+ File << "\n";
+ Indent(Elements.size());
+ File << "<?" << target << " " << content << "?>";
+
+ State = DEFAULT;
+}
+
+void cmWIXSourceWriter::AddAttribute(std::string const& key,
+ std::string const& value)
+{
+ File << " " << key << "=\"" << EscapeAttributeValue(value) << '"';
+}
+
+void cmWIXSourceWriter::AddAttributeUnlessEmpty(std::string const& key,
+ std::string const& value)
+{
+ if (!value.empty()) {
+ AddAttribute(key, value);
+ }
+}
+
+std::string cmWIXSourceWriter::CreateGuidFromComponentId(
+ std::string const& componentId)
+{
+ std::string guid = "*";
+ if (this->ComponentGuidType == CMAKE_GENERATED_GUID) {
+ std::string md5 = cmSystemTools::ComputeStringMD5(componentId);
+ cmUuid uuid;
+ std::vector<unsigned char> ns;
+ guid = uuid.FromMd5(ns, md5);
+ }
+ return guid;
+}
+
+void cmWIXSourceWriter::WriteXMLDeclaration()
+{
+ File << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
+}
+
+void cmWIXSourceWriter::Indent(size_t count)
+{
+ for (size_t i = 0; i < count; ++i) {
+ File << " ";
+ }
+}
+
+std::string cmWIXSourceWriter::EscapeAttributeValue(std::string const& value)
+{
+ std::string result;
+ result.reserve(value.size());
+
+ char c = 0;
+ for (size_t i = 0; i < value.size(); ++i) {
+ c = value[i];
+ switch (c) {
+ case '<':
+ result += "&lt;";
+ break;
+ case '>':
+ result += "&gt;";
+ break;
+ case '&':
+ result += "&amp;";
+ break;
+ case '"':
+ result += "&quot;";
+ break;
+ default:
+ result += c;
+ break;
+ }
+ }
+
+ return result;
+}
diff --git a/Source/CPack/WiX/cmWIXSourceWriter.h b/Source/CPack/WiX/cmWIXSourceWriter.h
new file mode 100644
index 0000000..45aefe5
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXSourceWriter.h
@@ -0,0 +1,80 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWIXSourceWriter_h
+#define cmWIXSourceWriter_h
+
+#include <CPack/cmCPackLog.h>
+
+#include <cmsys/FStream.hxx>
+
+#include <string>
+#include <vector>
+
+/** \class cmWIXSourceWriter
+ * \brief Helper class to generate XML WiX source files
+ */
+class cmWIXSourceWriter
+{
+public:
+ enum GuidType
+ {
+ WIX_GENERATED_GUID,
+ CMAKE_GENERATED_GUID
+ };
+
+ enum RootElementType
+ {
+ WIX_ELEMENT_ROOT,
+ INCLUDE_ELEMENT_ROOT
+ };
+
+ cmWIXSourceWriter(cmCPackLog* logger, std::string const& filename,
+ GuidType componentGuidType,
+ RootElementType rootElementType = WIX_ELEMENT_ROOT);
+
+ ~cmWIXSourceWriter();
+
+ void BeginElement(std::string const& name);
+
+ void EndElement(std::string const& name);
+
+ void AddTextNode(std::string const& text);
+
+ void AddProcessingInstruction(std::string const& target,
+ std::string const& content);
+
+ void AddAttribute(std::string const& key, std::string const& value);
+
+ void AddAttributeUnlessEmpty(std::string const& key,
+ std::string const& value);
+
+ std::string CreateGuidFromComponentId(std::string const& componentId);
+
+protected:
+ cmCPackLog* Logger;
+
+private:
+ enum State
+ {
+ DEFAULT,
+ BEGIN
+ };
+
+ void WriteXMLDeclaration();
+
+ void Indent(size_t count);
+
+ static std::string EscapeAttributeValue(std::string const& value);
+
+ cmsys::ofstream File;
+
+ State State;
+
+ std::vector<std::string> Elements;
+
+ std::string SourceFilename;
+
+ GuidType ComponentGuidType;
+};
+
+#endif