diff --git a/Modules/CPackWIX.cmake b/Modules/CPackWIX.cmake
index 237c5bc531..d9e0ba7177 100644
--- a/Modules/CPackWIX.cmake
+++ b/Modules/CPackWIX.cmake
@@ -116,6 +116,57 @@
# If this variable is not set, the default MSI template included with CMake
# will be used.
#
+# .. variable:: CPACK_WIX_PATCH_FILE
+#
+# Optional XML file with fragments to be inserted into generated WiX sources
+#
+# This optional variable can be used to specify an XML file that the
+# WiX generator will use to inject fragments into its generated
+# source files.
+#
+# Patch files understood by the CPack WiX generator
+# roughly follow this RELAX NG compact schema:
+#
+# .. code-block:: none
+#
+# start = CPackWiXPatch
+#
+# CPackWiXPatch = element CPackWiXPatch { CPackWiXFragment* }
+#
+# CPackWiXFragment = element CPackWiXFragment
+# {
+# attribute Id { string },
+# fragmentContent*
+# }
+#
+# fragmentContent = element * - CPackWiXFragment
+# {
+# (attribute * { text } | text | fragmentContent)*
+# }
+#
+# Currently fragments can be injected into most
+# Component, File and Directory elements.
+#
+# The following example illustrates how this works.
+#
+# Given that the WiX generator creates the following XML element:
+#
+# .. code-block:: xml
+#
+#
+#
+# The following XML patch file may be used to inject an Environment element
+# into it:
+#
+# .. code-block:: xml
+#
+#
+#
+#
+#
+#
+#
# .. variable:: CPACK_WIX_EXTRA_SOURCES
#
# Extra WiX source files
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 17fb52d484..3c944fbba6 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -517,6 +517,7 @@ if(WIN32)
CPack/WiX/cmCPackWIXGenerator.cxx
CPack/WiX/cmWIXSourceWriter.cxx
CPack/WiX/cmWIXRichTextFormatWriter.cxx
+ CPack/WiX/cmWIXPatchParser.cxx
)
endif()
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
index 1b9b20acef..6f1daaa0e6 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -218,6 +218,12 @@ bool cmCPackWIXGenerator::InitializeWiXConfiguration()
CollectExtensions("CPACK_WIX_EXTENSIONS", lightExtensions);
CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", lightExtensions);
+ const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE");
+ if(patchFilePath)
+ {
+ LoadPatchFragments(patchFilePath);
+ }
+
return true;
}
@@ -529,6 +535,28 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles()
wixSources.push_back(mainSourceFilePath);
+ 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.size())
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Some XML patch fragments did not have matching IDs: " <<
+ fragmentList << std::endl);
+ return false;
+ }
+
return true;
}
@@ -872,6 +900,7 @@ void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons(
packageExecutables,
shortcutMap);
+ ApplyPatchFragment(subDirectoryId, directoryDefinitions);
directoryDefinitions.EndElement("Directory");
}
else
@@ -891,7 +920,10 @@ void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons(
fileDefinitions.AddAttribute("Source", fullPath);
fileDefinitions.AddAttribute("KeyPath", "yes");
+ ApplyPatchFragment(fileId, fileDefinitions);
fileDefinitions.EndElement("File");
+
+ ApplyPatchFragment(componentId, fileDefinitions);
fileDefinitions.EndElement("Component");
fileDefinitions.EndElement("DirectoryRef");
@@ -1146,3 +1178,45 @@ void cmCPackWIXGenerator::CreateStartMenuFolder(
directoryDefinitions.EndElement("Directory");
}
+
+void cmCPackWIXGenerator::LoadPatchFragments(const std::string& patchFilePath)
+{
+ cmWIXPatchParser parser(fragments, Logger);
+ parser.ParseFile(patchFilePath.c_str());
+}
+
+void cmCPackWIXGenerator::ApplyPatchFragment(
+ const std::string& id, cmWIXSourceWriter& writer)
+{
+ cmWIXPatchParser::fragment_map_t::iterator i = fragments.find(id);
+ if(i == fragments.end()) return;
+
+ const cmWIXPatchElement& fragment = i->second;
+ for(cmWIXPatchElement::child_list_t::const_iterator
+ j = fragment.children.begin(); j != fragment.children.end(); ++j)
+ {
+ ApplyPatchElement(**j, writer);
+ }
+
+ fragments.erase(i);
+}
+
+void cmCPackWIXGenerator::ApplyPatchElement(
+ 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);
+ }
+
+ for(cmWIXPatchElement::child_list_t::const_iterator
+ i = element.children.begin(); i != element.children.end(); ++i)
+ {
+ ApplyPatchElement(**i, writer);
+ }
+
+ writer.EndElement(element.name);
+}
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h
index 84f68b6239..a0a057cbbe 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.h
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.h
@@ -13,6 +13,8 @@
#ifndef cmCPackWIXGenerator_h
#define cmCPackWIXGenerator_h
+#include "cmWIXPatchParser.h"
+
#include
#include
@@ -160,12 +162,21 @@ private:
void CreateStartMenuFolder(cmWIXSourceWriter& directoryDefinitions);
+ void LoadPatchFragments(const std::string& patchFilePath);
+
+ void ApplyPatchFragment(const std::string& id, cmWIXSourceWriter& writer);
+
+ void ApplyPatchElement(const cmWIXPatchElement& element,
+ cmWIXSourceWriter& writer);
+
std::vector wixSources;
id_map_t pathToIdMap;
ambiguity_map_t idAmbiguityCounter;
extension_set_t candleExtensions;
extension_set_t lightExtensions;
+
+ 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 0000000000..0a3b3bc2a1
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXPatchParser.cxx
@@ -0,0 +1,145 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2013 Kitware, Inc.
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#include "cmWIXPatchParser.h"
+
+#include
+
+#include
+
+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 char *name, const char **atts)
+{
+ std::string name_str = name;
+ if(state == BEGIN_DOCUMENT)
+ {
+ if(name_str == "CPackWiXPatch")
+ {
+ state = BEGIN_FRAGMENTS;
+ }
+ else
+ {
+ ReportValidationError("Expected root element 'CPackWiXPatch'");
+ }
+ }
+ else if(state == BEGIN_FRAGMENTS)
+ {
+ if(name_str == "CPackWiXFragment")
+ {
+ state = INSIDE_FRAGMENT;
+ StartFragment(atts);
+ }
+ else
+ {
+ ReportValidationError("Expected 'CPackWixFragment' element");
+ }
+ }
+ else if(state == INSIDE_FRAGMENT)
+ {
+ cmWIXPatchElement &parent = *elementStack.back();
+
+ parent.children.resize(parent.children.size() + 1);
+ cmWIXPatchElement*& currentElement = parent.children.back();
+ currentElement = new cmWIXPatchElement;
+ currentElement->name = name;
+
+ for(size_t i = 0; atts[i]; i += 2)
+ {
+ std::string key = atts[i];
+ std::string value = atts[i+1];
+
+ currentElement->attributes[key] = value;
+ }
+
+ elementStack.push_back(currentElement);
+ }
+}
+
+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::stringstream 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 char *name)
+{
+ std::string name_str = name;
+ if(state == INSIDE_FRAGMENT)
+ {
+ if(name_str == "CPackWiXFragment")
+ {
+ state = BEGIN_FRAGMENTS;
+ elementStack.clear();
+ }
+ else
+ {
+ elementStack.pop_back();
+ }
+ }
+}
+
+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(const std::string& message)
+{
+ ReportError(XML_GetCurrentLineNumber(Parser),
+ XML_GetCurrentColumnNumber(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 0000000000..4fa5e6fc54
--- /dev/null
+++ b/Source/CPack/WiX/cmWIXPatchParser.h
@@ -0,0 +1,75 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2013 Kitware, Inc.
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#ifndef cmCPackWIXPatchParser_h
+#define cmCPackWIXPatchParser_h
+
+#include
+
+#include
+
+#include