Quellcode durchsuchen

Merge topic 'wix-fragment-injection'

8632233 CPackWiX: allow customization of generated WiX sources
Brad King vor 12 Jahren
Ursprung
Commit
eb20fab736

+ 51 - 0
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

+ 1 - 0
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()
 

+ 74 - 0
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);
+}

+ 11 - 0
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

+ 145 - 0
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;
+}

+ 75 - 0
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

+ 2 - 0
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")

+ 7 - 0
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>