CPackWiX: allow customization of generated WiX sources
Added a new variable CPACK_WIX_PATCH_FILE that users can point at an XML patch file. Fragments defined within the patch file will be inserted at supported insertion points (currently Component, File and Directory).
This commit is contained in:
parent
b4fdbba55a
commit
8632233a2f
|
@ -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
|
||||
|
|
|
@ -517,6 +517,7 @@ if(WIN32)
|
|||
CPack/WiX/cmCPackWIXGenerator.cxx
|
||||
CPack/WiX/cmWIXSourceWriter.cxx
|
||||
CPack/WiX/cmWIXRichTextFormatWriter.cxx
|
||||
CPack/WiX/cmWIXPatchParser.cxx
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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")
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue