diff options
author | Nils Gladitz <gladitz@scivis.de> | 2012-10-03 14:08:49 (GMT) |
---|---|---|
committer | David Cole <david.cole@kitware.com> | 2012-12-03 16:00:31 (GMT) |
commit | 85baac1503c638756211ba07c4c25128e6d3d845 (patch) | |
tree | c10451adf45938e9bc6573d9edd09d4e3a67de67 /Source/CPack/WiX | |
parent | 581b0c0d078b5f07f68a53b118f44fc6e8313601 (diff) | |
download | CMake-85baac1503c638756211ba07c4c25128e6d3d845.zip CMake-85baac1503c638756211ba07c4c25128e6d3d845.tar.gz CMake-85baac1503c638756211ba07c4c25128e6d3d845.tar.bz2 |
CPack: Add a WiX Generator (#11575)
This new CPack generator produces an *.msi installer file.
Requires having the WiX Toolset installed in order to work
properly.
Download the WiX Toolset installer "WiX36.exe" here:
http://wix.codeplex.com/releases/view/93929
Diffstat (limited to 'Source/CPack/WiX')
-rw-r--r-- | Source/CPack/WiX/cmCPackWIXGenerator.cxx | 568 | ||||
-rw-r--r-- | Source/CPack/WiX/cmCPackWIXGenerator.h | 101 | ||||
-rw-r--r-- | Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx | 137 | ||||
-rw-r--r-- | Source/CPack/WiX/cmWIXRichTextFormatWriter.h | 46 | ||||
-rw-r--r-- | Source/CPack/WiX/cmWIXSourceWriter.cxx | 188 | ||||
-rw-r--r-- | Source/CPack/WiX/cmWIXSourceWriter.h | 67 |
6 files changed, 1107 insertions, 0 deletions
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx new file mode 100644 index 0000000..6e8c5ea --- /dev/null +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -0,0 +1,568 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2012 Kitware, Inc., Insight Software Consortium + + 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 "cmCPackWIXGenerator.h" + +#include <cmSystemTools.h> +#include <cmGeneratedFileStream.h> +#include <CPack/cmCPackLog.h> +#include <CPack/cmCPackComponentGroup.h> + +#include "cmWIXSourceWriter.h" +#include "cmWIXRichTextFormatWriter.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Directory.hxx> + +#include <rpc.h> // for GUID generation + +int cmCPackWIXGenerator::InitializeInternal() +{ + componentPackageMethod = ONE_PACKAGE; + + return this->Superclass::InitializeInternal(); +} + +bool cmCPackWIXGenerator::RunWiXCommand(const std::string& command) +{ + std::string cpackTopLevel; + if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel)) + { + return false; + } + + std::string logFileName = 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, + &returnValue, 0, cmSystemTools::OUTPUT_NONE); + + std::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( + const std::string& sourceFile, const std::string& objectFile) +{ + std::string executable; + if(!RequireOption("CPACK_WIX_CANDLE_EXECUTABLE", executable)) + { + return false; + } + + std::stringstream command; + command << QuotePath(executable); + command << " -nologo"; + command << " -arch " << GetArchitecture(); + command << " -out " << QuotePath(objectFile); + command << " " << QuotePath(sourceFile); + + return RunWiXCommand(command.str()); +} + +bool cmCPackWIXGenerator::RunLightCommand(const std::string& objectFiles) +{ + std::string executable; + if(!RequireOption("CPACK_WIX_LIGHT_EXECUTABLE", executable)) + { + return false; + } + + std::stringstream command; + command << QuotePath(executable); + command << " -nologo"; + command << " -out " << QuotePath(packageFileNames.at(0)); + command << " -ext WixUIExtension"; + 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); + } + + std::string cpackTopLevel; + if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel)) + { + return false; + } + + if(GetOption("CPACK_WIX_LICENSE_RTF") == 0) + { + std::string licenseFilename = cpackTopLevel + "/License.rtf"; + SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename.c_str()); + + if(!CreateLicenseFile()) + { + return false; + } + } + + return true; +} + +bool cmCPackWIXGenerator::PackageFilesImpl() +{ + if(!InitializeWiXConfiguration()) + { + return false; + } + + if(!CreateWiXVariablesIncludeFile()) + { + return false; + } + + if(!CreateWiXSourceFiles()) + { + return false; + } + + std::stringstream objectFiles; + for(std::size_t i = 0; i < wixSources.size(); ++i) + { + const std::string& sourceFilename = wixSources[i]; + + std::string objectFilename = + cmSystemTools::GetFilenameWithoutExtension(sourceFilename) + ".wixobj"; + + if(!RunCandleCommand(sourceFilename, objectFilename)) + { + return false; + } + + objectFiles << " " << QuotePath(objectFilename); + } + + return RunLightCommand(objectFiles.str()); +} + +bool cmCPackWIXGenerator::CreateWiXVariablesIncludeFile() +{ + std::string cpackTopLevel; + if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel)) + { + return false; + } + + std::string includeFilename = + cpackTopLevel + "/cpack_variables.wxi"; + + cmWIXSourceWriter includeFile(Logger, includeFilename, true); + 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"); + + return true; +} + +void cmCPackWIXGenerator::CopyDefinition( + cmWIXSourceWriter &source, const std::string &name) +{ + const char* value = GetOption(name.c_str()); + if(value) + { + AddDefinition(source, name, value); + } +} + +void cmCPackWIXGenerator::AddDefinition(cmWIXSourceWriter& source, + const std::string& name, const std::string& value) +{ + std::stringstream tmp; + tmp << name << "=\"" << value << '"'; + + source.AddProcessingInstruction("define", + cmWIXSourceWriter::WindowsCodepageToUtf8(tmp.str())); +} + +bool cmCPackWIXGenerator::CreateWiXSourceFiles() +{ + std::string cpackTopLevel; + if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel)) + { + return false; + } + + std::string directoryDefinitionsFilename = + cpackTopLevel + "/directories.wxs"; + + wixSources.push_back(directoryDefinitionsFilename); + + cmWIXSourceWriter directoryDefinitions(Logger, directoryDefinitionsFilename); + directoryDefinitions.BeginElement("Fragment"); + + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", "TARGETDIR"); + directoryDefinitions.AddAttribute("Name", "SourceDir"); + + directoryDefinitions.BeginElement("Directory"); + if(GetArchitecture() == "x86") + { + directoryDefinitions.AddAttribute("Id", "ProgramFilesFolder"); + } + else + { + directoryDefinitions.AddAttribute("Id", "ProgramFiles64Folder"); + } + + std::vector<std::string> install_root; + + std::string tmp; + if(!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY", tmp)) + { + return false; + } + + cmSystemTools::SplitPath(tmp.c_str(), install_root); + + if(!install_root.empty() && install_root.back().empty()) + { + install_root.pop_back(); + } + + for(std::size_t i = 1; i < install_root.size(); ++i) + { + directoryDefinitions.BeginElement("Directory"); + + if(i == install_root.size() - 1) + { + directoryDefinitions.AddAttribute("Id", "INSTALL_ROOT"); + } + else + { + std::stringstream tmp; + tmp << "INSTALL_PREFIX_" << i; + directoryDefinitions.AddAttribute("Id", tmp.str()); + } + + directoryDefinitions.AddAttribute("Name", install_root[i]); + } + + std::size_t directoryCounter = 0; + std::size_t fileCounter = 0; + + std::string fileDefinitionsFilename = + cpackTopLevel + "/files.wxs"; + + wixSources.push_back(fileDefinitionsFilename); + + cmWIXSourceWriter fileDefinitions(Logger, fileDefinitionsFilename); + fileDefinitions.BeginElement("Fragment"); + + std::string featureDefinitionsFilename = + cpackTopLevel +"/features.wxs"; + + wixSources.push_back(featureDefinitionsFilename); + + cmWIXSourceWriter featureDefinitions(Logger, featureDefinitionsFilename); + featureDefinitions.BeginElement("Fragment"); + + featureDefinitions.BeginElement("Feature"); + featureDefinitions.AddAttribute("Id", "ProductFeature"); + featureDefinitions.AddAttribute("Title", Name); + featureDefinitions.AddAttribute("Level", "1"); + featureDefinitions.EndElement(); + + featureDefinitions.BeginElement("FeatureRef"); + featureDefinitions.AddAttribute("Id", "ProductFeature"); + + AddDirectoryAndFileDefinitons( + toplevel, "INSTALL_ROOT", + directoryDefinitions, fileDefinitions, featureDefinitions, + directoryCounter, fileCounter); + + featureDefinitions.EndElement(); + featureDefinitions.EndElement(); + fileDefinitions.EndElement(); + + for(std::size_t i = 1; i < install_root.size(); ++i) + { + directoryDefinitions.EndElement(); + } + + directoryDefinitions.EndElement(); + directoryDefinitions.EndElement(); + directoryDefinitions.EndElement(); + + std::string wixTemplate = FindTemplate("WIX.template.in"); + if(wixTemplate.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Could not find CPack WiX template file WIX.template.in" << std::endl); + return false; + } + + std::string mainSourceFilePath = cpackTopLevel + "/main.wxs"; + + if(!ConfigureFile(wixTemplate.c_str(), mainSourceFilePath .c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Failed creating '" << mainSourceFilePath << + "'' from template." << std::endl); + + return false; + } + + wixSources.push_back(mainSourceFilePath); + + 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); + + std::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( + const std::string& topdir, + const std::string& directoryId, + cmWIXSourceWriter& directoryDefinitions, + cmWIXSourceWriter& fileDefinitions, + cmWIXSourceWriter& featureDefinitions, + std::size_t& directoryCounter, + std::size_t& fileCounter) +{ + cmsys::Directory dir; + dir.Load(topdir.c_str()); + + for(std::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; + + if(cmSystemTools::FileIsDirectory(fullPath.c_str())) + { + std::stringstream tmp; + tmp << "DIR_ID_" << ++directoryCounter; + std::string subDirectoryId = tmp.str(); + + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", subDirectoryId); + directoryDefinitions.AddAttribute("Name", fileName); + + AddDirectoryAndFileDefinitons( + fullPath, subDirectoryId, + directoryDefinitions, + fileDefinitions, + featureDefinitions, + directoryCounter, + fileCounter); + + directoryDefinitions.EndElement(); + } + else + { + std::stringstream tmp; + tmp << "_ID_" << ++fileCounter; + std::string idSuffix = tmp.str(); + + std::string componentId = std::string("CMP") + idSuffix; + std::string fileId = std::string("FILE") + idSuffix; + + fileDefinitions.BeginElement("DirectoryRef"); + fileDefinitions.AddAttribute("Id", directoryId); + + fileDefinitions.BeginElement("Component"); + fileDefinitions.AddAttribute("Id", componentId); + fileDefinitions.AddAttribute("Guid", "*"); + + fileDefinitions.BeginElement("File"); + fileDefinitions.AddAttribute("Id", fileId); + fileDefinitions.AddAttribute("Source", fullPath); + fileDefinitions.AddAttribute("KeyPath", "yes"); + + fileDefinitions.EndElement(); + fileDefinitions.EndElement(); + fileDefinitions.EndElement(); + + featureDefinitions.BeginElement("ComponentRef"); + featureDefinitions.AddAttribute("Id", componentId); + featureDefinitions.EndElement(); + } + } +} + +bool cmCPackWIXGenerator::RequireOption( + const std::string& 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); + + RPC_CSTR tmp = 0; + UuidToStringA(&guid, &tmp); + + std::string result(reinterpret_cast<char*>(tmp)); + RpcStringFree(&tmp); + + return cmSystemTools::UpperCase(result); +} + +std::string cmCPackWIXGenerator::QuotePath(const std::string& path) +{ + return std::string("\"") + path + '"'; +} + +std::string cmCPackWIXGenerator::GetRightmostExtension( + const std::string& filename) +{ + std::string extension; + + std::string::size_type i = filename.rfind("."); + if(i != std::string::npos) + { + extension = filename.substr(i); + } + + return cmSystemTools::LowerCase(extension); +} diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h new file mode 100644 index 0000000..9fc9093 --- /dev/null +++ b/Source/CPack/WiX/cmCPackWIXGenerator.h @@ -0,0 +1,101 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2012 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 cmCPackWIXGenerator_h +#define cmCPackWIXGenerator_h + +#include <CPack/cmCPackGenerator.h> + +#include <string> +#include <map> + +class cmWIXSourceWriter; + +/** \class cmCPackWIXGenerator + * \brief A generator for WIX files + */ +class cmCPackWIXGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackWIXGenerator, cmCPackGenerator); + +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 false; + } + +private: + bool InitializeWiXConfiguration(); + + bool PackageFilesImpl(); + + bool CreateWiXVariablesIncludeFile(); + + void CopyDefinition( + cmWIXSourceWriter &source, const std::string &name); + + void AddDefinition(cmWIXSourceWriter& source, + const std::string& name, const std::string& value); + + bool CreateWiXSourceFiles(); + + bool CreateLicenseFile(); + + bool RunWiXCommand(const std::string& command); + + bool RunCandleCommand( + const std::string& sourceFile, const std::string& objectFile); + + bool RunLightCommand(const std::string& objectFiles); + + void AddDirectoryAndFileDefinitons(const std::string& topdir, + const std::string& directoryId, + cmWIXSourceWriter& directoryDefinitions, + cmWIXSourceWriter& fileDefinitions, + cmWIXSourceWriter& featureDefinitions, + std::size_t& directoryCounter, + std::size_t& fileCounter); + + bool RequireOption(const std::string& name, std::string& value) const; + + std::string GetArchitecture() const; + + static std::string GenerateGUID(); + + static std::string QuotePath(const std::string& path); + + static std::string GetRightmostExtension(const std::string& filename); + + std::vector<std::string> wixSources; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx new file mode 100644 index 0000000..0763344 --- /dev/null +++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx @@ -0,0 +1,137 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2012 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 "cmWIXRichTextFormatWriter.h" + +#include <cmVersion.h> + +cmWIXRichTextFormatWriter::cmWIXRichTextFormatWriter( + const std::string& 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(const std::string& text) +{ + typedef unsigned char rtf_byte_t; + + for(std::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 + { + file << "[NON-ASCII-" << int(c) << "]"; + } + } + break; + } + } +} + +void cmWIXRichTextFormatWriter::WriteHeader() +{ + ControlWord("rtf1"); + ControlWord("ansi"); + ControlWord("ansicpg1252"); + ControlWord("deff0"); + ControlWord("deflang1031"); + + WriteFontTable(); + WriteGenerator(); +} + +void cmWIXRichTextFormatWriter::WriteFontTable() +{ + StartGroup(); + ControlWord("fonttbl"); + + StartGroup(); + ControlWord("f0"); + ControlWord("fswiss"); + ControlWord("fcharset0 Arial;"); + EndGroup(); + + 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(const std::string& keyword) +{ + file << "\\" << keyword; +} + +void cmWIXRichTextFormatWriter::NewControlWord(const std::string& keyword) +{ + file << "\\*\\" << keyword; +} + +void cmWIXRichTextFormatWriter::StartGroup() +{ + file.put('{'); +} + +void cmWIXRichTextFormatWriter::EndGroup() +{ + file.put('}'); +} diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.h b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h new file mode 100644 index 0000000..10b67c3 --- /dev/null +++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h @@ -0,0 +1,46 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2012 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 cmWIXRichTextFormatWriter_h +#define cmWIXRichTextFormatWriter_h + +#include <fstream> + +/** \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(const std::string& filename); + ~cmWIXRichTextFormatWriter(); + + void AddText(const std::string& text); + +private: + void WriteHeader(); + void WriteFontTable(); + void WriteGenerator(); + + void WriteDocumentPrefix(); + + void ControlWord(const std::string& keyword); + void NewControlWord(const std::string& keyword); + + void StartGroup(); + void EndGroup(); + + std::ofstream file; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx new file mode 100644 index 0000000..cc00e57 --- /dev/null +++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx @@ -0,0 +1,188 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 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 "cmWIXSourceWriter.h" + +#include <CPack/cmCPackGenerator.h> + +#include <windows.h> + +cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger, + const std::string& filename, + bool isIncludeFile): + Logger(logger), + file(filename.c_str()), + state(DEFAULT) +{ + WriteXMLDeclaration(); + + if(isIncludeFile) + { + BeginElement("Include"); + } + else + { + BeginElement("Wix"); + } + + AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi"); +} + +cmWIXSourceWriter::~cmWIXSourceWriter() +{ + while(elements.size()) + { + EndElement(); + } +} + +void cmWIXSourceWriter::BeginElement(const std::string& name) +{ + if(state == BEGIN) + { + file << ">"; + } + + file << "\n"; + Indent(elements.size()); + file << "<" << name; + + elements.push_back(name); + state = BEGIN; +} + +void cmWIXSourceWriter::EndElement() +{ + if(elements.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "can not end WiX element with no open elements" << std::endl); + return; + } + + if(state == DEFAULT) + { + file << "\n"; + Indent(elements.size()-1); + file << "</" << elements.back() << ">"; + } + else + { + file << "/>"; + } + + elements.pop_back(); + state = DEFAULT; +} + +void cmWIXSourceWriter::AddProcessingInstruction( + const std::string& target, const std::string& content) +{ + if(state == BEGIN) + { + file << ">"; + } + + file << "\n"; + Indent(elements.size()); + file << "<?" << target << " " << content << "?>"; + + state = DEFAULT; +} + +void cmWIXSourceWriter::AddAttribute( + const std::string& key, const std::string& value) +{ + std::string utf8 = WindowsCodepageToUtf8(value); + + file << " " << key << "=\"" << EscapeAttributeValue(utf8) << '"'; +} + +std::string cmWIXSourceWriter::WindowsCodepageToUtf8(const std::string& value) +{ + if(value.empty()) + { + return std::string(); + } + + int characterCount = MultiByteToWideChar( + CP_ACP, 0, value.c_str(), value.size(), 0, 0); + + if(characterCount == 0) + { + return std::string(); + } + + std::vector<wchar_t> utf16(characterCount); + + MultiByteToWideChar( + CP_ACP, 0, value.c_str(), value.size(), &utf16[0], utf16.size()); + + int utf8ByteCount = + WideCharToMultiByte(CP_UTF8, 0, &utf16[0], utf16.size(), 0, 0, 0, 0); + + if(utf8ByteCount == 0) + { + return std::string(); + } + + std::vector<char> utf8(utf8ByteCount); + + WideCharToMultiByte(CP_UTF8, 0, &utf16[0], utf16.size(), + &utf8[0], utf8.size(), 0, 0); + + return std::string(&utf8[0], utf8.size()); +} + + +void cmWIXSourceWriter::WriteXMLDeclaration() +{ + file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; +} + +void cmWIXSourceWriter::Indent(std::size_t count) +{ + for(std::size_t i = 0; i < count; ++i) + { + file << " "; + } +} + +std::string cmWIXSourceWriter::EscapeAttributeValue( + const std::string& value) +{ + std::string result; + result.reserve(value.size()); + + char c = 0; + for(std::size_t i = 0 ; i < value.size(); ++i) + { + c = value[i]; + switch(c) + { + case '<': + result += "<"; + break; + case '&': + result +="&"; + break; + case '"': + result += """; + 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..582554d --- /dev/null +++ b/Source/CPack/WiX/cmWIXSourceWriter.h @@ -0,0 +1,67 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 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 cmWIXSourceWriter_h +#define cmWIXSourceWriter_h + +#include <vector> +#include <string> +#include <fstream> + +#include <CPack/cmCPackLog.h> + +/** \class cmWIXSourceWriter + * \brief Helper class to generate XML WiX source files + */ +class cmWIXSourceWriter +{ +public: + cmWIXSourceWriter(cmCPackLog* logger, + const std::string& filename, bool isIncludeFile = false); + + ~cmWIXSourceWriter(); + + void BeginElement(const std::string& name); + + void EndElement(); + + void AddProcessingInstruction( + const std::string& target, const std::string& content); + + void AddAttribute( + const std::string& key, const std::string& value); + + static std::string WindowsCodepageToUtf8(const std::string& value); + +private: + enum State + { + DEFAULT, + BEGIN + }; + + void WriteXMLDeclaration(); + + void Indent(std::size_t count); + + static std::string EscapeAttributeValue(const std::string& value); + + std::ofstream file; + + std::vector<std::string> elements; + + State state; + + cmCPackLog* Logger; +}; + +#endif |