summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Modules/CPackWIX.cmake51
-rw-r--r--Source/CMakeLists.txt1
-rw-r--r--Source/CPack/WiX/cmCPackWIXGenerator.cxx74
-rw-r--r--Source/CPack/WiX/cmCPackWIXGenerator.h11
-rw-r--r--Source/CPack/WiX/cmWIXPatchParser.cxx145
-rw-r--r--Source/CPack/WiX/cmWIXPatchParser.h75
-rw-r--r--Tests/CPackWiXGenerator/CMakeLists.txt2
-rw-r--r--Tests/CPackWiXGenerator/patch.xml7
8 files changed, 366 insertions, 0 deletions
diff --git a/Modules/CPackWIX.cmake b/Modules/CPackWIX.cmake
index 237c5bc..d9e0ba7 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
+#
+# <Component Id="CM_CP_applications.bin.my_libapp.exe" Guid="*"/>
+#
+# The following XML patch file may be used to inject an Environment element
+# into it:
+#
+# .. code-block:: xml
+#
+# <CPackWiXPatch>
+# <CPackWiXFragment Id="CM_CP_applications.bin.my_libapp.exe">
+# <Environment Id="MyEnvironment" Action="set"
+# Name="MyVariableName" Value="MyVariableValue"/>
+# </CPackWiXFragment>
+# </CPackWiXPatch>
+#
# .. variable:: CPACK_WIX_EXTRA_SOURCES
#
# Extra WiX source files
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 17fb52d..3c944fb 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 1b9b20a..6f1daaa 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 84f68b6..a0a057c 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 <CPack/cmCPackGenerator.h>
#include <string>
@@ -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<std::string> 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 0000000..0a3b3bc
--- /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 <CPack/cmCPackGenerator.h>
+
+#include <cm_expat.h>
+
+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 0000000..4fa5e6f
--- /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 <cmXMLParser.h>
+
+#include <CPack/cmCPackLog.h>
+
+#include <map>
+#include <list>
+
+struct cmWIXPatchElement
+{
+ ~cmWIXPatchElement();
+
+ typedef std::list<cmWIXPatchElement*> 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 char *name, const char **atts);
+
+ void StartFragment(const char **attributes);
+
+ virtual void EndElement(const char *name);
+ virtual void ReportError(int line, int column, const char* msg);
+
+ void ReportValidationError(const std::string& 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/Tests/CPackWiXGenerator/CMakeLists.txt b/Tests/CPackWiXGenerator/CMakeLists.txt
index ecfecdb..0b06045 100644
--- a/Tests/CPackWiXGenerator/CMakeLists.txt
+++ b/Tests/CPackWiXGenerator/CMakeLists.txt
@@ -49,6 +49,8 @@ set(CPACK_PACKAGE_EXECUTABLES
"my-other-app" "Second CPack WiX Test"
)
+set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/patch.xml")
+
include(CPack)
cpack_add_install_type(Full DISPLAY_NAME "Everything")
diff --git a/Tests/CPackWiXGenerator/patch.xml b/Tests/CPackWiXGenerator/patch.xml
new file mode 100644
index 0000000..13c392d
--- /dev/null
+++ b/Tests/CPackWiXGenerator/patch.xml
@@ -0,0 +1,7 @@
+<CPackWiXPatch>
+ <CPackWiXFragment Id="CM_CP_applications.bin.my_libapp.exe">
+ <Environment Id="MyEnvironment" Action="set"
+ Name="CPackWiXGeneratorTest"
+ Value="CPackWiXGeneratorTest"/>
+ </CPackWiXFragment>
+</CPackWiXPatch>