Browse Source

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
Nils Gladitz 13 years ago
parent
commit
85baac1503

+ 24 - 0
CMakeCPackOptions.cmake.in

@@ -51,3 +51,27 @@ if("${CPACK_GENERATOR}" STREQUAL "PackageMaker")
     set(CPACK_PACKAGE_DEFAULT_LOCATION "/usr")
   endif()
 endif()
+
+if("${CPACK_GENERATOR}" STREQUAL "WIX")
+  # Reset CPACK_PACKAGE_VERSION to deal with WiX restriction.
+  # But the file names still use the full CMake_VERSION value:
+  set(CPACK_PACKAGE_FILE_NAME
+    "${CPACK_PACKAGE_NAME}-@CMake_VERSION@-${CPACK_SYSTEM_NAME}")
+  set(CPACK_SOURCE_PACKAGE_FILE_NAME
+    "${CPACK_PACKAGE_NAME}-@CMake_VERSION@-Source")
+
+  if(NOT CPACK_WIX_SIZEOF_VOID_P)
+    set(CPACK_WIX_SIZEOF_VOID_P "@CMAKE_SIZEOF_VOID_P@")
+  endif()
+
+  set(CPACK_PACKAGE_VERSION
+    "@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@.@CMake_VERSION_PATCH@")
+  # WIX installers require at most a 4 component version number, where
+  # each component is an integer between 0 and 65534 inclusive
+  set(tweak "@CMake_VERSION_TWEAK@")
+  if(tweak MATCHES "^[0-9]+$")
+    if(tweak GREATER 0 AND tweak LESS 65535)
+      set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}.${tweak}")
+    endif()
+  endif()
+endif()

+ 6 - 1
Modules/CPack.cmake

@@ -438,6 +438,7 @@ if(NOT CPACK_GENERATOR)
     endif()
   else()
     option(CPACK_BINARY_NSIS "Enable to build NSIS packages" ON)
+    option(CPACK_BINARY_WIX  "Enable to build WiX packages" OFF)
     option(CPACK_BINARY_ZIP  "Enable to build ZIP packages" OFF)
   endif()
 
@@ -453,6 +454,7 @@ if(NOT CPACK_GENERATOR)
   cpack_optional_append(CPACK_GENERATOR  CPACK_BINARY_TGZ          TGZ)
   cpack_optional_append(CPACK_GENERATOR  CPACK_BINARY_TBZ2         TBZ2)
   cpack_optional_append(CPACK_GENERATOR  CPACK_BINARY_TZ           TZ)
+  cpack_optional_append(CPACK_GENERATOR  CPACK_BINARY_WIX          WIX)
   cpack_optional_append(CPACK_GENERATOR  CPACK_BINARY_ZIP          ZIP)
 
 endif()
@@ -483,7 +485,7 @@ endif()
 mark_as_advanced(CPACK_BINARY_CYGWIN CPACK_BINARY_PACKAGEMAKER CPACK_BINARY_OSXX11
                  CPACK_BINARY_STGZ   CPACK_BINARY_TGZ          CPACK_BINARY_TBZ2
                  CPACK_BINARY_DEB    CPACK_BINARY_RPM          CPACK_BINARY_TZ
-                 CPACK_BINARY_NSIS CPACK_BINARY_ZIP CPACK_BINARY_BUNDLE
+                 CPACK_BINARY_NSIS CPACK_BINARY_WIX CPACK_BINARY_ZIP CPACK_BINARY_BUNDLE
                  CPACK_SOURCE_CYGWIN CPACK_SOURCE_TBZ2 CPACK_SOURCE_TGZ
                  CPACK_SOURCE_TZ CPACK_SOURCE_ZIP CPACK_BINARY_DRAGNDROP)
 
@@ -522,6 +524,9 @@ cpack_set_if_not_set(CPACK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
 cpack_set_if_not_set(CPACK_NSIS_INSTALLER_ICON_CODE "")
 cpack_set_if_not_set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "")
 
+# WiX specific variables
+cpack_set_if_not_set(CPACK_WIX_SIZEOF_VOID_P "${CMAKE_SIZEOF_VOID_P}")
+
 if(DEFINED CPACK_COMPONENTS_ALL)
   if(CPACK_MONOLITHIC_INSTALL)
     message("CPack warning: both CPACK_COMPONENTS_ALL and CPACK_MONOLITHIC_INSTALL have been set.\nDefaulting to a monolithic installation.")

+ 83 - 0
Modules/CPackWIX.cmake

@@ -0,0 +1,83 @@
+##section Variables specific to CPack WiX generator
+##end
+##module
+# - CPack WiX generator specific options
+#
+# The following variables are specific to the installers built
+# on Windows using WiX.
+##end
+##variable
+#  CPACK_WIX_UPGRADE_GUID - Upgrade GUID (Product/@UpgradeCode)
+#
+# Will be automatically generated unless explicitly provided.
+#
+# It should be explicitly set to a constant generated
+# gloabally unique identifier (GUID) to allow your installers
+# to replace existing installations that use the same GUID.
+#
+# You may for example explicitly set this variable in
+# your CMakeLists.txt to the value that has been generated per default.
+# You should not use GUIDs that you did not generate yourself or which may
+# belong to other projects.
+#
+# A GUID shall have the following fixed length syntax:
+# XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+#  (each X represents an uppercase hexadecimal digit)
+##end
+##variable
+#  CPACK_WIX_PRODUCT_GUID - Product GUID (Product/@Id)
+#
+# Will be automatically generated unless explicitly provided.
+#
+# If explicitly provided this will set the Product Id of your installer.
+#
+# The installer will abort if it detects a pre-existing installation that uses
+# the same GUID.
+#
+# The GUID shall use the syntax described for CPACK_WIX_UPGRADE_GUID.
+##end
+##variable
+#  CPACK_WIX_LICENSE_RTF - RTF License File
+#
+# If CPACK_RESOURCE_FILE_LICENSE has an .rtf extension
+# it is used as-is.
+#
+# If CPACK_RESOURCE_FILE_LICENSE has an .txt extension
+# it is implicitly converted to RTF by the WiX Generator.
+#
+# With CPACK_WIX_LICENSE_RTF you can override the license file used
+# by the WiX Generator in case CPACK_RESOURCE_FILE_LICENSE
+# is in an unsupported format or the .txt -> .rtf
+# conversion does not work as expected.
+##end
+
+#=============================================================================
+# 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.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+if(NOT CPACK_WIX_ROOT)
+  file(TO_CMAKE_PATH "$ENV{WIX}" CPACK_WIX_ROOT)
+endif()
+
+find_program(CPACK_WIX_CANDLE_EXECUTABLE candle
+  PATHS "${CPACK_WIX_ROOT}/bin")
+
+if(NOT CPACK_WIX_CANDLE_EXECUTABLE)
+  message(FATAL_ERROR "Could not find the WiX candle executable.")
+endif()
+
+find_program(CPACK_WIX_LIGHT_EXECUTABLE light
+  PATHS "${CPACK_WIX_ROOT}/bin")
+
+if(NOT CPACK_WIX_LIGHT_EXECUTABLE)
+  message(FATAL_ERROR "Could not find the WiX light executable.")
+endif()

+ 31 - 0
Modules/WIX.template.in

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?include "cpack_variables.wxi"?>
+
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
+    RequiredVersion="3.6.3303.0">
+
+    <Product Id="$(var.CPACK_WIX_PRODUCT_GUID)"
+        Name="$(var.CPACK_PACKAGE_NAME)"
+        Language="1033"
+        Version="$(var.CPACK_PACKAGE_VERSION)"
+        Manufacturer="$(var.CPACK_PACKAGE_VENDOR)"
+        UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)">
+
+        <Package InstallerVersion="301" Compressed="yes"/>
+
+        <Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/>
+
+        <MajorUpgrade
+            Schedule="afterInstallInitialize"
+            AllowSameVersionUpgrades="yes"
+            DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."/>
+
+        <WixVariable Id="WixUILicenseRtf" Value="$(var.CPACK_WIX_LICENSE_RTF)"/>
+        <Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/>
+
+        <FeatureRef Id="ProductFeature"/>
+
+        <UIRef Id="WixUI_InstallDir" />
+    </Product>
+</Wix>

+ 8 - 0
Source/CMakeLists.txt

@@ -499,6 +499,14 @@ if(UNIX)
     )
 endif()
 
+if(WIN32)
+  set(CPACK_SRCS ${CPACK_SRCS}
+    CPack/WiX/cmCPackWIXGenerator.cxx
+    CPack/WiX/cmWIXSourceWriter.cxx
+    CPack/WiX/cmWIXRichTextFormatWriter.cxx
+  )
+endif()
+
 if(APPLE)
   set(CPACK_SRCS ${CPACK_SRCS}
     CPack/cmCPackBundleGenerator.cxx

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

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

+ 137 - 0
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('}');
+}

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

+ 188 - 0
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 += "&lt;";
+      break;
+    case '&':
+      result +="&amp;";
+      break;
+    case '"':
+      result += "&quot;";
+      break;
+    default:
+      result += c;
+      break;
+      }
+    }
+
+  return result;
+}

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

+ 11 - 0
Source/CPack/cmCPackGeneratorFactory.cxx

@@ -19,6 +19,7 @@
 #include "cmCPackZIPGenerator.h"
 #include "cmCPackSTGZGenerator.h"
 #include "cmCPackNSISGenerator.h"
+
 #ifdef __APPLE__
 #  include "cmCPackDragNDropGenerator.h"
 #  include "cmCPackBundleGenerator.h"
@@ -37,6 +38,9 @@
 #  include "cmCPackRPMGenerator.h"
 #endif
 
+#ifdef _WIN32
+#  include "WiX/cmCPackWIXGenerator.h"
+#endif
 
 #include "cmCPackLog.h"
 
@@ -82,6 +86,13 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory()
     this->RegisterGenerator("ZIP", "ZIP file format",
       cmCPackZIPGenerator::CreateGenerator);
     }
+#ifdef _WIN32
+  if (cmCPackWIXGenerator::CanGenerate())
+    {
+    this->RegisterGenerator("WIX", "MSI file format via WiX tools",
+      cmCPackWIXGenerator::CreateGenerator);
+    }
+#endif
   if (cmCPackTarBZip2Generator::CanGenerate())
     {
     this->RegisterGenerator("TBZ2", "Tar BZip2 compression",

+ 23 - 0
Tests/CMakeLists.txt

@@ -697,6 +697,29 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
     endif()
   endif()
 
+  # On Windows run the CPackWiXGenerator test
+  # if the WiX Toolset seems to be available
+  if(WIN32)
+    file(TO_CMAKE_PATH "$ENV{WIX}" WIX_ROOT)
+
+    find_program(WIX_LIGHT_EXECUTABLE light
+      PATHS "${WIX_ROOT}/bin"
+      DOC "WiX Toolset light.exe location")
+
+    if(WIX_LIGHT_EXECUTABLE)
+      add_test(CPackWiXGenerator ${CMAKE_CTEST_COMMAND}
+        --build-and-test
+        "${CMake_SOURCE_DIR}/Tests/CPackWiXGenerator"
+        "${CMake_BINARY_DIR}/Tests/CPackWiXGenerator"
+        --build-generator ${CMAKE_TEST_GENERATOR}
+        --build-project CPackWiXGenerator
+        --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
+        --test-command ${CMAKE_CMAKE_COMMAND}
+          "-DCPackWiXGenerator_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/CPackWiXGenerator"
+          -P "${CMake_SOURCE_DIR}/Tests/CPackWiXGenerator/RunCPackVerifyResult.cmake")
+    endif()
+  endif()
+
   if(CTEST_RUN_CPackComponents)
     set(CPackComponents_EXTRA_OPTIONS)
     if(APPLE)

+ 73 - 0
Tests/CPackWiXGenerator/CMakeLists.txt

@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(CPackWiXGenerator)
+
+add_library(mylib mylib.cpp)
+
+add_executable(mylibapp mylibapp.cpp)
+target_link_libraries(mylibapp mylib)
+
+install(TARGETS mylib
+  ARCHIVE
+  DESTINATION lib
+  COMPONENT libraries)
+
+install(TARGETS mylibapp
+  RUNTIME
+  DESTINATION bin
+  COMPONENT applications)
+
+install(FILES mylib.h "file with spaces.h"
+  DESTINATION include
+  COMPONENT headers)
+
+set(CPACK_GENERATOR "WIX")
+
+set(CPACK_PACKAGE_NAME "MyLib")
+set(CPACK_PACKAGE_VENDOR "CMake.org")
+set(CPACK_PACKAGE_CONTACT "[email protected]")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
+  "MyLib - CPack Component Installation Example")
+
+set(CPACK_PACKAGE_VERSION_MAJOR "1")
+set(CPACK_PACKAGE_VERSION_MINOR "0")
+set(CPACK_PACKAGE_VERSION_PATCH "0")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example")
+
+set(CPACK_WIX_UPGRADE_GUID "BF20CE5E-7F7C-401D-8F7C-AB45E8D170E6")
+
+include(CPack)
+
+cpack_add_install_type(Full DISPLAY_NAME "Everything")
+cpack_add_install_type(Developer)
+
+cpack_add_component_group(Runtime)
+
+cpack_add_component_group(Development
+  EXPANDED
+  DESCRIPTION "All of the tools you'll ever need to develop software")
+
+cpack_add_component(applications
+  DISPLAY_NAME "MyLib Application"
+  DESCRIPTION "An extremely useful application that makes use of MyLib"
+  GROUP Runtime
+  INSTALL_TYPES Full)
+
+cpack_add_component(documentation
+  DISPLAY_NAME "MyLib Documentation"
+  DESCRIPTION "The extensive suite of MyLib Application documentation files"
+  GROUP Runtime
+  INSTALL_TYPES Full)
+
+cpack_add_component(libraries
+  DISPLAY_NAME "Libraries"
+  DESCRIPTION "Static libraries used to build programs with MyLib"
+  GROUP Development
+  INSTALL_TYPES Developer Full)
+
+cpack_add_component(headers
+  DISPLAY_NAME "C++ Headers"
+  DESCRIPTION "C/C++ header files for use with MyLib"
+  GROUP Development
+  DEPENDS libraries
+  INSTALL_TYPES Developer Full)

+ 72 - 0
Tests/CPackWiXGenerator/RunCPackVerifyResult.cmake

@@ -0,0 +1,72 @@
+message(STATUS "=============================================================")
+message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
+message(STATUS "")
+
+if(NOT CPackWiXGenerator_BINARY_DIR)
+  message(FATAL_ERROR "CPackWiXGenerator_BINARY_DIR not set")
+endif()
+
+message(STATUS "CMAKE_COMMAND: ${CMAKE_COMMAND}")
+message(STATUS "CMAKE_CPACK_COMMAND: ${CMAKE_CPACK_COMMAND}")
+message(STATUS "CPackWiXGenerator_BINARY_DIR: ${CPackWiXGenerator_BINARY_DIR}")
+
+execute_process(COMMAND "${CMAKE_CPACK_COMMAND}"
+  RESULT_VARIABLE CPack_result
+  OUTPUT_VARIABLE CPack_output
+  ERROR_VARIABLE CPack_error
+  WORKING_DIRECTORY "${CPackWiXGenerator_BINARY_DIR}")
+
+if(CPack_result)
+  message(FATAL_ERROR "CPack execution went wrong!, CPack_output=${CPack_output}, CPack_error=${CPack_error}")
+else ()
+  message(STATUS "CPack_output=${CPack_output}")
+endif()
+
+set(expected_file_mask "*.msi")
+file(GLOB installer_file "${expected_file_mask}")
+
+message(STATUS "installer_file='${installer_file}'")
+message(STATUS "expected_file_mask='${expected_file_mask}'")
+
+if(NOT installer_file)
+  message(FATAL_ERROR "installer_file does not exist.")
+endif()
+
+function(run_wix_command command)
+  file(TO_CMAKE_PATH "$ENV{WIX}" WIX_ROOT)
+  set(WIX_PROGRAM "${WIX_ROOT}/bin/${command}.exe")
+
+  if(NOT EXISTS "${WIX_PROGRAM}")
+    message(FATAL_ERROR "Failed to find WiX Tool: ${WIX_PROGRAM}")
+  endif()
+
+  message(STATUS "Running WiX Tool: ${command} ${ARGN}")
+
+  execute_process(COMMAND "${WIX_PROGRAM}" ${ARGN}
+    RESULT_VARIABLE WIX_result
+    OUTPUT_VARIABLE WIX_output
+    ERROR_VARIABLE WIX_output
+    WORKING_DIRECTORY "${CPackWiXGenerator_BINARY_DIR}")
+
+  message(STATUS "${command} Output: \n${WIX_output}")
+
+  if(WIX_result)
+    message(FATAL_ERROR "WiX ${command} failed: ${WIX_result}")
+  endif()
+endfunction()
+
+file(GLOB WXS_SOURCE_FILES
+  "${CPackWiXGenerator_BINARY_DIR}/_CPack_Packages/*/WIX/*.wxs")
+
+if(NOT WXS_SOURCE_FILES)
+  message(FATAL_ERROR "Failed finding WiX source files to validate.")
+endif()
+
+foreach(WXS_SOURCE_FILE IN LISTS WXS_SOURCE_FILES)
+  run_wix_command(wixcop "${WXS_SOURCE_FILE}")
+endforeach()
+
+# error SMOK1076 : ICE61: This product should remove only older
+# versions of itself. The Maximum version is not less
+# than the current product. (1.0.0 1.0.0)
+run_wix_command(smoke -nologo -wx -sw1076 "${installer_file}")

+ 0 - 0
Tests/CPackWiXGenerator/file with spaces.h


+ 7 - 0
Tests/CPackWiXGenerator/mylib.cpp

@@ -0,0 +1,7 @@
+#include "mylib.h"
+#include "stdio.h"
+
+void mylib_function()
+{
+  printf("This is mylib");
+}

+ 1 - 0
Tests/CPackWiXGenerator/mylib.h

@@ -0,0 +1 @@
+void mylib_function();

+ 6 - 0
Tests/CPackWiXGenerator/mylibapp.cpp

@@ -0,0 +1,6 @@
+#include "mylib.h"
+
+int main()
+{
+  mylib_function();
+}