| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmCPackWIXGenerator.h" |
| |
| #include "cmCPackComponentGroup.h" |
| #include "cmCPackLog.h" |
| #include "cmCryptoHash.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmInstalledFile.h" |
| #include "cmSystemTools.h" |
| #include "cmUuid.h" |
| #include <algorithm> |
| |
| #include "cmWIXDirectoriesSourceWriter.h" |
| #include "cmWIXFeaturesSourceWriter.h" |
| #include "cmWIXFilesSourceWriter.h" |
| #include "cmWIXRichTextFormatWriter.h" |
| #include "cmWIXSourceWriter.h" |
| |
| #include "cmsys/Directory.hxx" |
| #include "cmsys/Encoding.hxx" |
| #include "cmsys/FStream.hxx" |
| #include "cmsys/SystemTools.hxx" |
| |
| #ifdef _WIN32 |
| # include <rpc.h> // for GUID generation (windows only) |
| #else |
| # include <uuid/uuid.h> // for GUID generation (libuuid) |
| #endif |
| |
| #include "cmCMakeToWixPath.h" |
| |
| cmCPackWIXGenerator::cmCPackWIXGenerator() |
| : Patch(0) |
| , ComponentGuidType(cmWIXSourceWriter::WIX_GENERATED_GUID) |
| { |
| } |
| |
| cmCPackWIXGenerator::~cmCPackWIXGenerator() |
| { |
| if (this->Patch) { |
| delete this->Patch; |
| } |
| } |
| |
| int cmCPackWIXGenerator::InitializeInternal() |
| { |
| componentPackageMethod = ONE_PACKAGE; |
| this->Patch = new cmWIXPatch(this->Logger); |
| |
| return this->Superclass::InitializeInternal(); |
| } |
| |
| bool cmCPackWIXGenerator::RunWiXCommand(std::string const& command) |
| { |
| std::string logFileName = this->CPackTopLevel + "/wix.log"; |
| |
| cmCPackLogger(cmCPackLog::LOG_DEBUG, |
| "Running WiX command: " << command << std::endl); |
| |
| std::string output; |
| |
| int returnValue = 0; |
| bool status = cmSystemTools::RunSingleCommand( |
| command, &output, &output, &returnValue, 0, cmSystemTools::OUTPUT_NONE); |
| |
| cmsys::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(std::string const& sourceFile, |
| std::string const& objectFile) |
| { |
| std::string executable; |
| if (!RequireOption("CPACK_WIX_CANDLE_EXECUTABLE", executable)) { |
| return false; |
| } |
| |
| std::ostringstream command; |
| command << QuotePath(executable); |
| command << " -nologo"; |
| command << " -arch " << GetArchitecture(); |
| command << " -out " << QuotePath(objectFile); |
| |
| for (std::string const& ext : CandleExtensions) { |
| command << " -ext " << QuotePath(ext); |
| } |
| |
| if (sourceFile.rfind(this->CPackTopLevel, 0) != 0) { |
| command << " " << QuotePath("-I" + this->CPackTopLevel); |
| } |
| |
| AddCustomFlags("CPACK_WIX_CANDLE_EXTRA_FLAGS", command); |
| |
| command << " " << QuotePath(sourceFile); |
| |
| return RunWiXCommand(command.str()); |
| } |
| |
| bool cmCPackWIXGenerator::RunLightCommand(std::string const& objectFiles) |
| { |
| std::string executable; |
| if (!RequireOption("CPACK_WIX_LIGHT_EXECUTABLE", executable)) { |
| return false; |
| } |
| |
| std::ostringstream command; |
| command << QuotePath(executable); |
| command << " -nologo"; |
| command << " -out " << QuotePath(CMakeToWixPath(packageFileNames.at(0))); |
| |
| for (std::string const& ext : this->LightExtensions) { |
| command << " -ext " << QuotePath(ext); |
| } |
| |
| const char* const cultures = GetOption("CPACK_WIX_CULTURES"); |
| if (cultures) { |
| command << " -cultures:" << cultures; |
| } |
| |
| AddCustomFlags("CPACK_WIX_LIGHT_EXTRA_FLAGS", command); |
| |
| 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("Internal/CPack/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); |
| } |
| |
| if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY", this->CPackTopLevel)) { |
| return false; |
| } |
| |
| if (GetOption("CPACK_WIX_LICENSE_RTF") == 0) { |
| std::string licenseFilename = this->CPackTopLevel + "/License.rtf"; |
| SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename.c_str()); |
| |
| if (!CreateLicenseFile()) { |
| return false; |
| } |
| } |
| |
| if (GetOption("CPACK_PACKAGE_VENDOR") == 0) { |
| std::string defaultVendor = "Humanity"; |
| SetOption("CPACK_PACKAGE_VENDOR", defaultVendor.c_str()); |
| |
| cmCPackLogger(cmCPackLog::LOG_VERBOSE, |
| "CPACK_PACKAGE_VENDOR implicitly set to " |
| << defaultVendor << " . " << std::endl); |
| } |
| |
| if (GetOption("CPACK_WIX_UI_REF") == 0) { |
| std::string defaultRef = "WixUI_InstallDir"; |
| |
| if (!this->Components.empty()) { |
| defaultRef = "WixUI_FeatureTree"; |
| } |
| |
| SetOption("CPACK_WIX_UI_REF", defaultRef.c_str()); |
| } |
| |
| const char* packageContact = GetOption("CPACK_PACKAGE_CONTACT"); |
| if (packageContact != 0 && GetOption("CPACK_WIX_PROPERTY_ARPCONTACT") == 0) { |
| SetOption("CPACK_WIX_PROPERTY_ARPCONTACT", packageContact); |
| } |
| |
| CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions); |
| CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions); |
| |
| this->LightExtensions.insert("WixUIExtension"); |
| CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions); |
| CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions); |
| |
| const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE"); |
| if (patchFilePath) { |
| std::vector<std::string> patchFilePaths; |
| cmSystemTools::ExpandListArgument(patchFilePath, patchFilePaths); |
| |
| for (std::string const& p : patchFilePaths) { |
| if (!this->Patch->LoadFragments(p)) { |
| return false; |
| } |
| } |
| } |
| |
| // if install folder is supposed to be set absolutely, the default |
| // component guid "*" cannot be used |
| if (cmSystemTools::IsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) { |
| this->ComponentGuidType = cmWIXSourceWriter::CMAKE_GENERATED_GUID; |
| } |
| |
| return true; |
| } |
| |
| bool cmCPackWIXGenerator::PackageFilesImpl() |
| { |
| if (!InitializeWiXConfiguration()) { |
| return false; |
| } |
| |
| CreateWiXVariablesIncludeFile(); |
| CreateWiXPropertiesIncludeFile(); |
| CreateWiXProductFragmentIncludeFile(); |
| |
| if (!CreateWiXSourceFiles()) { |
| return false; |
| } |
| |
| AppendUserSuppliedExtraSources(); |
| |
| std::set<std::string> usedBaseNames; |
| |
| std::ostringstream objectFiles; |
| for (std::string const& sourceFilename : this->WixSources) { |
| std::string baseName = |
| cmSystemTools::GetFilenameWithoutLastExtension(sourceFilename); |
| |
| unsigned int counter = 0; |
| std::string uniqueBaseName = baseName; |
| |
| while (usedBaseNames.find(uniqueBaseName) != usedBaseNames.end()) { |
| std::ostringstream tmp; |
| tmp << baseName << ++counter; |
| uniqueBaseName = tmp.str(); |
| } |
| |
| usedBaseNames.insert(uniqueBaseName); |
| |
| std::string objectFilename = |
| this->CPackTopLevel + "/" + uniqueBaseName + ".wixobj"; |
| |
| if (!RunCandleCommand(CMakeToWixPath(sourceFilename), |
| CMakeToWixPath(objectFilename))) { |
| return false; |
| } |
| |
| objectFiles << " " << QuotePath(CMakeToWixPath(objectFilename)); |
| } |
| |
| AppendUserSuppliedExtraObjects(objectFiles); |
| |
| return RunLightCommand(objectFiles.str()); |
| } |
| |
| void cmCPackWIXGenerator::AppendUserSuppliedExtraSources() |
| { |
| const char* cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES"); |
| if (!cpackWixExtraSources) |
| return; |
| |
| cmSystemTools::ExpandListArgument(cpackWixExtraSources, this->WixSources); |
| } |
| |
| void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream) |
| { |
| const char* cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS"); |
| if (!cpackWixExtraObjects) |
| return; |
| |
| std::vector<std::string> expandedExtraObjects; |
| |
| cmSystemTools::ExpandListArgument(cpackWixExtraObjects, |
| expandedExtraObjects); |
| |
| for (std::string const& obj : expandedExtraObjects) { |
| stream << " " << QuotePath(obj); |
| } |
| } |
| |
| void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile() |
| { |
| std::string includeFilename = this->CPackTopLevel + "/cpack_variables.wxi"; |
| |
| cmWIXSourceWriter includeFile(this->Logger, includeFilename, |
| this->ComponentGuidType, |
| cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); |
| |
| 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", DefinitionType::PATH); |
| CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_ICON", DefinitionType::PATH); |
| CopyDefinition(includeFile, "CPACK_WIX_UI_BANNER", DefinitionType::PATH); |
| CopyDefinition(includeFile, "CPACK_WIX_UI_DIALOG", DefinitionType::PATH); |
| SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER", |
| GetOption("CPACK_PACKAGE_NAME")); |
| CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER"); |
| CopyDefinition(includeFile, "CPACK_WIX_UI_REF"); |
| } |
| |
| void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile() |
| { |
| std::string includeFilename = this->CPackTopLevel + "/properties.wxi"; |
| |
| cmWIXSourceWriter includeFile(this->Logger, includeFilename, |
| this->ComponentGuidType, |
| cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); |
| |
| std::string prefix = "CPACK_WIX_PROPERTY_"; |
| std::vector<std::string> options = GetOptions(); |
| |
| for (std::string const& name : options) { |
| if (name.length() > prefix.length() && |
| name.substr(0, prefix.length()) == prefix) { |
| std::string id = name.substr(prefix.length()); |
| std::string value = GetOption(name.c_str()); |
| |
| includeFile.BeginElement("Property"); |
| includeFile.AddAttribute("Id", id); |
| includeFile.AddAttribute("Value", value); |
| includeFile.EndElement("Property"); |
| } |
| } |
| |
| if (GetOption("CPACK_WIX_PROPERTY_ARPINSTALLLOCATION") == 0) { |
| includeFile.BeginElement("Property"); |
| includeFile.AddAttribute("Id", "INSTALL_ROOT"); |
| includeFile.AddAttribute("Secure", "yes"); |
| |
| includeFile.BeginElement("RegistrySearch"); |
| includeFile.AddAttribute("Id", "FindInstallLocation"); |
| includeFile.AddAttribute("Root", "HKLM"); |
| includeFile.AddAttribute( |
| "Key", |
| "Software\\Microsoft\\Windows\\" |
| "CurrentVersion\\Uninstall\\[WIX_UPGRADE_DETECTED]"); |
| includeFile.AddAttribute("Name", "InstallLocation"); |
| includeFile.AddAttribute("Type", "raw"); |
| includeFile.EndElement("RegistrySearch"); |
| includeFile.EndElement("Property"); |
| |
| includeFile.BeginElement("SetProperty"); |
| includeFile.AddAttribute("Id", "ARPINSTALLLOCATION"); |
| includeFile.AddAttribute("Value", "[INSTALL_ROOT]"); |
| includeFile.AddAttribute("After", "CostFinalize"); |
| includeFile.EndElement("SetProperty"); |
| } |
| } |
| |
| void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile() |
| { |
| std::string includeFilename = this->CPackTopLevel + "/product_fragment.wxi"; |
| |
| cmWIXSourceWriter includeFile(this->Logger, includeFilename, |
| this->ComponentGuidType, |
| cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); |
| |
| this->Patch->ApplyFragment("#PRODUCT", includeFile); |
| } |
| |
| void cmCPackWIXGenerator::CopyDefinition(cmWIXSourceWriter& source, |
| std::string const& name, |
| DefinitionType type) |
| { |
| const char* value = GetOption(name.c_str()); |
| if (value) { |
| if (type == DefinitionType::PATH) { |
| AddDefinition(source, name, CMakeToWixPath(value)); |
| } else { |
| AddDefinition(source, name, value); |
| } |
| } |
| } |
| |
| void cmCPackWIXGenerator::AddDefinition(cmWIXSourceWriter& source, |
| std::string const& name, |
| std::string const& value) |
| { |
| std::ostringstream tmp; |
| tmp << name << "=\"" << value << '"'; |
| |
| source.AddProcessingInstruction("define", tmp.str()); |
| } |
| |
| bool cmCPackWIXGenerator::CreateWiXSourceFiles() |
| { |
| // if install folder is supposed to be set absolutely, the default |
| // component guid "*" cannot be used |
| std::string directoryDefinitionsFilename = |
| this->CPackTopLevel + "/directories.wxs"; |
| |
| this->WixSources.push_back(directoryDefinitionsFilename); |
| |
| cmWIXDirectoriesSourceWriter directoryDefinitions( |
| this->Logger, directoryDefinitionsFilename, this->ComponentGuidType); |
| directoryDefinitions.BeginElement("Fragment"); |
| |
| std::string installRoot; |
| if (!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY", installRoot)) { |
| return false; |
| } |
| |
| directoryDefinitions.BeginElement("Directory"); |
| directoryDefinitions.AddAttribute("Id", "TARGETDIR"); |
| directoryDefinitions.AddAttribute("Name", "SourceDir"); |
| |
| size_t installRootSize = |
| directoryDefinitions.BeginInstallationPrefixDirectory(GetRootFolderId(), |
| installRoot); |
| |
| std::string fileDefinitionsFilename = this->CPackTopLevel + "/files.wxs"; |
| |
| this->WixSources.push_back(fileDefinitionsFilename); |
| |
| cmWIXFilesSourceWriter fileDefinitions(this->Logger, fileDefinitionsFilename, |
| this->ComponentGuidType); |
| |
| fileDefinitions.BeginElement("Fragment"); |
| |
| std::string featureDefinitionsFilename = |
| this->CPackTopLevel + "/features.wxs"; |
| |
| this->WixSources.push_back(featureDefinitionsFilename); |
| |
| cmWIXFeaturesSourceWriter featureDefinitions( |
| this->Logger, featureDefinitionsFilename, this->ComponentGuidType); |
| |
| featureDefinitions.BeginElement("Fragment"); |
| |
| featureDefinitions.BeginElement("Feature"); |
| featureDefinitions.AddAttribute("Id", "ProductFeature"); |
| featureDefinitions.AddAttribute("Display", "expand"); |
| featureDefinitions.AddAttribute("Absent", "disallow"); |
| featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT"); |
| |
| std::string cpackPackageName; |
| if (!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) { |
| return false; |
| } |
| |
| std::string featureTitle = cpackPackageName; |
| if (const char* title = GetOption("CPACK_WIX_ROOT_FEATURE_TITLE")) { |
| featureTitle = title; |
| } |
| featureDefinitions.AddAttribute("Title", featureTitle); |
| if (const char* desc = GetOption("CPACK_WIX_ROOT_FEATURE_DESCRIPTION")) { |
| featureDefinitions.AddAttribute("Description", desc); |
| } |
| featureDefinitions.AddAttribute("Level", "1"); |
| this->Patch->ApplyFragment("#PRODUCTFEATURE", featureDefinitions); |
| |
| const char* package = GetOption("CPACK_WIX_CMAKE_PACKAGE_REGISTRY"); |
| if (package) { |
| featureDefinitions.CreateCMakePackageRegistryEntry( |
| package, GetOption("CPACK_WIX_UPGRADE_GUID")); |
| } |
| |
| if (!CreateFeatureHierarchy(featureDefinitions)) { |
| return false; |
| } |
| |
| featureDefinitions.EndElement("Feature"); |
| |
| std::set<cmWIXShortcuts::Type> emittedShortcutTypes; |
| |
| cmWIXShortcuts globalShortcuts; |
| if (Components.empty()) { |
| AddComponentsToFeature(toplevel, "ProductFeature", directoryDefinitions, |
| fileDefinitions, featureDefinitions, |
| globalShortcuts); |
| |
| globalShortcuts.AddShortcutTypes(emittedShortcutTypes); |
| } else { |
| for (auto const& i : this->Components) { |
| cmCPackComponent const& component = i.second; |
| |
| std::string componentPath = toplevel; |
| componentPath += "/"; |
| componentPath += component.Name; |
| |
| std::string const componentFeatureId = "CM_C_" + component.Name; |
| |
| cmWIXShortcuts featureShortcuts; |
| AddComponentsToFeature(componentPath, componentFeatureId, |
| directoryDefinitions, fileDefinitions, |
| featureDefinitions, featureShortcuts); |
| |
| featureShortcuts.AddShortcutTypes(emittedShortcutTypes); |
| |
| if (!CreateShortcuts(component.Name, componentFeatureId, |
| featureShortcuts, false, fileDefinitions, |
| featureDefinitions)) { |
| return false; |
| } |
| } |
| } |
| |
| bool emitUninstallShortcut = |
| emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) != |
| emittedShortcutTypes.end(); |
| |
| if (!CreateShortcuts(std::string(), "ProductFeature", globalShortcuts, |
| emitUninstallShortcut, fileDefinitions, |
| featureDefinitions)) { |
| return false; |
| } |
| |
| featureDefinitions.EndElement("Fragment"); |
| fileDefinitions.EndElement("Fragment"); |
| |
| directoryDefinitions.EndInstallationPrefixDirectory(installRootSize); |
| |
| if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) != |
| emittedShortcutTypes.end()) { |
| directoryDefinitions.EmitStartMenuFolder( |
| GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER")); |
| } |
| |
| if (emittedShortcutTypes.find(cmWIXShortcuts::DESKTOP) != |
| emittedShortcutTypes.end()) { |
| directoryDefinitions.EmitDesktopFolder(); |
| } |
| |
| if (emittedShortcutTypes.find(cmWIXShortcuts::STARTUP) != |
| emittedShortcutTypes.end()) { |
| directoryDefinitions.EmitStartupFolder(); |
| } |
| |
| directoryDefinitions.EndElement("Directory"); |
| directoryDefinitions.EndElement("Fragment"); |
| |
| if (!GenerateMainSourceFileFromTemplate()) { |
| return false; |
| } |
| |
| return this->Patch->CheckForUnappliedFragments(); |
| } |
| |
| std::string cmCPackWIXGenerator::GetRootFolderId() const |
| { |
| if (cmSystemTools::IsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) { |
| return ""; |
| } |
| |
| std::string result = "ProgramFiles<64>Folder"; |
| |
| const char* rootFolderId = GetOption("CPACK_WIX_ROOT_FOLDER_ID"); |
| if (rootFolderId) { |
| result = rootFolderId; |
| } |
| |
| if (GetArchitecture() == "x86") { |
| cmSystemTools::ReplaceString(result, "<64>", ""); |
| } else { |
| cmSystemTools::ReplaceString(result, "<64>", "64"); |
| } |
| |
| return result; |
| } |
| |
| bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate() |
| { |
| std::string wixTemplate = FindTemplate("WIX.template.in"); |
| if (GetOption("CPACK_WIX_TEMPLATE") != 0) { |
| wixTemplate = GetOption("CPACK_WIX_TEMPLATE"); |
| } |
| |
| if (wixTemplate.empty()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Could not find CPack WiX template file WIX.template.in" |
| << std::endl); |
| return false; |
| } |
| |
| std::string mainSourceFilePath = this->CPackTopLevel + "/main.wxs"; |
| |
| if (!ConfigureFile(wixTemplate, mainSourceFilePath)) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Failed creating '" << mainSourceFilePath |
| << "'' from template." << std::endl); |
| |
| return false; |
| } |
| |
| this->WixSources.push_back(mainSourceFilePath); |
| |
| return true; |
| } |
| |
| bool cmCPackWIXGenerator::CreateFeatureHierarchy( |
| cmWIXFeaturesSourceWriter& featureDefinitions) |
| { |
| for (auto const& i : ComponentGroups) { |
| cmCPackComponentGroup const& group = i.second; |
| if (group.ParentGroup == 0) { |
| featureDefinitions.EmitFeatureForComponentGroup(group, *this->Patch); |
| } |
| } |
| |
| for (auto const& i : this->Components) { |
| cmCPackComponent const& component = i.second; |
| |
| if (!component.Group) { |
| featureDefinitions.EmitFeatureForComponent(component, *this->Patch); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmCPackWIXGenerator::AddComponentsToFeature( |
| std::string const& rootPath, std::string const& featureId, |
| cmWIXDirectoriesSourceWriter& directoryDefinitions, |
| cmWIXFilesSourceWriter& fileDefinitions, |
| cmWIXFeaturesSourceWriter& featureDefinitions, cmWIXShortcuts& shortcuts) |
| { |
| featureDefinitions.BeginElement("FeatureRef"); |
| featureDefinitions.AddAttribute("Id", featureId); |
| |
| std::vector<std::string> cpackPackageExecutablesList; |
| const char* cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES"); |
| if (cpackPackageExecutables) { |
| cmSystemTools::ExpandListArgument(cpackPackageExecutables, |
| cpackPackageExecutablesList); |
| if (cpackPackageExecutablesList.size() % 2 != 0) { |
| cmCPackLogger( |
| cmCPackLog::LOG_ERROR, |
| "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and " |
| "<text label>." |
| << std::endl); |
| return false; |
| } |
| } |
| |
| std::vector<std::string> cpackPackageDesktopLinksList; |
| const char* cpackPackageDesktopLinks = |
| GetOption("CPACK_CREATE_DESKTOP_LINKS"); |
| if (cpackPackageDesktopLinks) { |
| cmSystemTools::ExpandListArgument(cpackPackageDesktopLinks, |
| cpackPackageDesktopLinksList); |
| } |
| |
| AddDirectoryAndFileDefinitions( |
| rootPath, "INSTALL_ROOT", directoryDefinitions, fileDefinitions, |
| featureDefinitions, cpackPackageExecutablesList, |
| cpackPackageDesktopLinksList, shortcuts); |
| |
| featureDefinitions.EndElement("FeatureRef"); |
| |
| return true; |
| } |
| |
| bool cmCPackWIXGenerator::CreateShortcuts( |
| std::string const& cpackComponentName, std::string const& featureId, |
| cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut, |
| cmWIXFilesSourceWriter& fileDefinitions, |
| cmWIXFeaturesSourceWriter& featureDefinitions) |
| { |
| if (!shortcuts.empty(cmWIXShortcuts::START_MENU)) { |
| if (!this->CreateShortcutsOfSpecificType( |
| cmWIXShortcuts::START_MENU, cpackComponentName, featureId, "", |
| shortcuts, emitUninstallShortcut, fileDefinitions, |
| featureDefinitions)) { |
| return false; |
| } |
| } |
| |
| if (!shortcuts.empty(cmWIXShortcuts::DESKTOP)) { |
| if (!this->CreateShortcutsOfSpecificType( |
| cmWIXShortcuts::DESKTOP, cpackComponentName, featureId, "DESKTOP", |
| shortcuts, false, fileDefinitions, featureDefinitions)) { |
| return false; |
| } |
| } |
| |
| if (!shortcuts.empty(cmWIXShortcuts::STARTUP)) { |
| if (!this->CreateShortcutsOfSpecificType( |
| cmWIXShortcuts::STARTUP, cpackComponentName, featureId, "STARTUP", |
| shortcuts, false, fileDefinitions, featureDefinitions)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmCPackWIXGenerator::CreateShortcutsOfSpecificType( |
| cmWIXShortcuts::Type type, std::string const& cpackComponentName, |
| std::string const& featureId, std::string const& idPrefix, |
| cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut, |
| cmWIXFilesSourceWriter& fileDefinitions, |
| cmWIXFeaturesSourceWriter& featureDefinitions) |
| { |
| std::string directoryId; |
| switch (type) { |
| case cmWIXShortcuts::START_MENU: |
| directoryId = "PROGRAM_MENU_FOLDER"; |
| break; |
| case cmWIXShortcuts::DESKTOP: |
| directoryId = "DesktopFolder"; |
| break; |
| case cmWIXShortcuts::STARTUP: |
| directoryId = "StartupFolder"; |
| break; |
| default: |
| return false; |
| } |
| |
| featureDefinitions.BeginElement("FeatureRef"); |
| featureDefinitions.AddAttribute("Id", featureId); |
| |
| std::string cpackVendor; |
| if (!RequireOption("CPACK_PACKAGE_VENDOR", cpackVendor)) { |
| return false; |
| } |
| |
| std::string cpackPackageName; |
| if (!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) { |
| return false; |
| } |
| |
| std::string idSuffix; |
| if (!cpackComponentName.empty()) { |
| idSuffix += "_"; |
| idSuffix += cpackComponentName; |
| } |
| |
| std::string componentId = "CM_SHORTCUT"; |
| if (idPrefix.size()) { |
| componentId += "_" + idPrefix; |
| } |
| |
| componentId += idSuffix; |
| |
| fileDefinitions.BeginElement("DirectoryRef"); |
| fileDefinitions.AddAttribute("Id", directoryId); |
| |
| fileDefinitions.BeginElement("Component"); |
| fileDefinitions.AddAttribute("Id", componentId); |
| fileDefinitions.AddAttribute( |
| "Guid", fileDefinitions.CreateGuidFromComponentId(componentId)); |
| |
| this->Patch->ApplyFragment(componentId, fileDefinitions); |
| |
| std::string registryKey = |
| std::string("Software\\") + cpackVendor + "\\" + cpackPackageName; |
| |
| shortcuts.EmitShortcuts(type, registryKey, cpackComponentName, |
| fileDefinitions); |
| |
| if (type == cmWIXShortcuts::START_MENU) { |
| fileDefinitions.EmitRemoveFolder("CM_REMOVE_PROGRAM_MENU_FOLDER" + |
| idSuffix); |
| } |
| |
| if (emitUninstallShortcut) { |
| fileDefinitions.EmitUninstallShortcut(cpackPackageName); |
| } |
| |
| fileDefinitions.EndElement("Component"); |
| fileDefinitions.EndElement("DirectoryRef"); |
| |
| featureDefinitions.EmitComponentRef(componentId); |
| featureDefinitions.EndElement("FeatureRef"); |
| |
| 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); |
| |
| cmsys::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::AddDirectoryAndFileDefinitions( |
| std::string const& topdir, std::string const& directoryId, |
| cmWIXDirectoriesSourceWriter& directoryDefinitions, |
| cmWIXFilesSourceWriter& fileDefinitions, |
| cmWIXFeaturesSourceWriter& featureDefinitions, |
| std::vector<std::string> const& packageExecutables, |
| std::vector<std::string> const& desktopExecutables, |
| cmWIXShortcuts& shortcuts) |
| { |
| cmsys::Directory dir; |
| dir.Load(topdir.c_str()); |
| |
| std::string relativeDirectoryPath = |
| cmSystemTools::RelativePath(toplevel.c_str(), topdir.c_str()); |
| |
| if (relativeDirectoryPath.empty()) { |
| relativeDirectoryPath = "."; |
| } |
| |
| cmInstalledFile const* directoryInstalledFile = this->GetInstalledFile( |
| this->RelativePathWithoutComponentPrefix(relativeDirectoryPath)); |
| |
| bool emptyDirectory = dir.GetNumberOfFiles() == 2; |
| bool createDirectory = false; |
| |
| if (emptyDirectory) { |
| createDirectory = true; |
| } |
| |
| if (directoryInstalledFile) { |
| if (directoryInstalledFile->HasProperty("CPACK_WIX_ACL")) { |
| createDirectory = true; |
| } |
| } |
| |
| if (createDirectory) { |
| std::string componentId = fileDefinitions.EmitComponentCreateFolder( |
| directoryId, GenerateGUID(), directoryInstalledFile); |
| featureDefinitions.EmitComponentRef(componentId); |
| } |
| |
| if (emptyDirectory) { |
| return; |
| } |
| |
| for (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; |
| |
| std::string relativePath = |
| cmSystemTools::RelativePath(toplevel.c_str(), fullPath.c_str()); |
| |
| std::string id = PathToId(relativePath); |
| |
| if (cmSystemTools::FileIsDirectory(fullPath.c_str())) { |
| std::string subDirectoryId = std::string("CM_D") + id; |
| |
| directoryDefinitions.BeginElement("Directory"); |
| directoryDefinitions.AddAttribute("Id", subDirectoryId); |
| directoryDefinitions.AddAttribute("Name", fileName); |
| this->Patch->ApplyFragment(subDirectoryId, directoryDefinitions); |
| |
| AddDirectoryAndFileDefinitions( |
| fullPath, subDirectoryId, directoryDefinitions, fileDefinitions, |
| featureDefinitions, packageExecutables, desktopExecutables, shortcuts); |
| |
| directoryDefinitions.EndElement("Directory"); |
| } else { |
| cmInstalledFile const* installedFile = this->GetInstalledFile( |
| this->RelativePathWithoutComponentPrefix(relativePath)); |
| |
| if (installedFile) { |
| shortcuts.CreateFromProperties(id, directoryId, *installedFile); |
| } |
| |
| std::string componentId = fileDefinitions.EmitComponentFile( |
| directoryId, id, fullPath, *(this->Patch), installedFile); |
| |
| featureDefinitions.EmitComponentRef(componentId); |
| |
| for (size_t j = 0; j < packageExecutables.size(); ++j) { |
| std::string const& executableName = packageExecutables[j++]; |
| std::string const& textLabel = packageExecutables[j]; |
| |
| if (cmSystemTools::LowerCase(fileName) == |
| cmSystemTools::LowerCase(executableName) + ".exe") { |
| cmWIXShortcut shortcut; |
| shortcut.label = textLabel; |
| shortcut.workingDirectoryId = directoryId; |
| shortcuts.insert(cmWIXShortcuts::START_MENU, id, shortcut); |
| |
| if (!desktopExecutables.empty() && |
| std::find(desktopExecutables.begin(), desktopExecutables.end(), |
| executableName) != desktopExecutables.end()) { |
| shortcuts.insert(cmWIXShortcuts::DESKTOP, id, shortcut); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| bool cmCPackWIXGenerator::RequireOption(std::string const& 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() |
| { |
| #ifdef _WIN32 |
| UUID guid; |
| UuidCreate(&guid); |
| |
| unsigned short* tmp = 0; |
| UuidToStringW(&guid, &tmp); |
| |
| std::string result = |
| cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(tmp)); |
| RpcStringFreeW(&tmp); |
| #else |
| uuid_t guid; |
| char guid_ch[37] = { 0 }; |
| |
| uuid_generate(guid); |
| uuid_unparse(guid, guid_ch); |
| std::string result = guid_ch; |
| #endif |
| |
| return cmSystemTools::UpperCase(result); |
| } |
| |
| std::string cmCPackWIXGenerator::QuotePath(std::string const& path) |
| { |
| return std::string("\"") + path + '"'; |
| } |
| |
| std::string cmCPackWIXGenerator::GetRightmostExtension( |
| std::string const& filename) |
| { |
| std::string extension; |
| |
| std::string::size_type i = filename.rfind("."); |
| if (i != std::string::npos) { |
| extension = filename.substr(i); |
| } |
| |
| return cmSystemTools::LowerCase(extension); |
| } |
| |
| std::string cmCPackWIXGenerator::PathToId(std::string const& path) |
| { |
| id_map_t::const_iterator i = PathToIdMap.find(path); |
| if (i != PathToIdMap.end()) |
| return i->second; |
| |
| std::string id = CreateNewIdForPath(path); |
| return id; |
| } |
| |
| std::string cmCPackWIXGenerator::CreateNewIdForPath(std::string const& path) |
| { |
| std::vector<std::string> components; |
| cmSystemTools::SplitPath(path.c_str(), components, false); |
| |
| size_t replacementCount = 0; |
| |
| std::string identifier; |
| std::string currentComponent; |
| |
| for (size_t i = 1; i < components.size(); ++i) { |
| if (i != 1) |
| identifier += '.'; |
| |
| currentComponent = |
| NormalizeComponentForId(components[i], replacementCount); |
| |
| identifier += currentComponent; |
| } |
| |
| std::string idPrefix = "P"; |
| size_t replacementPercent = replacementCount * 100 / identifier.size(); |
| if (replacementPercent > 33 || identifier.size() > 60) { |
| identifier = CreateHashedId(path, currentComponent); |
| idPrefix = "H"; |
| } |
| |
| std::ostringstream result; |
| result << idPrefix << "_" << identifier; |
| |
| size_t ambiguityCount = ++IdAmbiguityCounter[identifier]; |
| |
| if (ambiguityCount > 999) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Error while trying to generate a unique Id for '" |
| << path << "'" << std::endl); |
| |
| return std::string(); |
| } else if (ambiguityCount > 1) { |
| result << "_" << ambiguityCount; |
| } |
| |
| std::string resultString = result.str(); |
| |
| PathToIdMap[path] = resultString; |
| |
| return resultString; |
| } |
| |
| std::string cmCPackWIXGenerator::CreateHashedId( |
| std::string const& path, std::string const& normalizedFilename) |
| { |
| cmCryptoHash sha1(cmCryptoHash::AlgoSHA1); |
| std::string const hash = sha1.HashString(path); |
| |
| std::string identifier; |
| identifier += hash.substr(0, 7) + "_"; |
| |
| const size_t maxFileNameLength = 52; |
| if (normalizedFilename.length() > maxFileNameLength) { |
| identifier += normalizedFilename.substr(0, maxFileNameLength - 3); |
| identifier += "..."; |
| } else { |
| identifier += normalizedFilename; |
| } |
| |
| return identifier; |
| } |
| |
| std::string cmCPackWIXGenerator::NormalizeComponentForId( |
| std::string const& component, size_t& replacementCount) |
| { |
| std::string result; |
| result.resize(component.size()); |
| |
| for (size_t i = 0; i < component.size(); ++i) { |
| char c = component[i]; |
| if (IsLegalIdCharacter(c)) { |
| result[i] = c; |
| } else { |
| result[i] = '_'; |
| ++replacementCount; |
| } |
| } |
| |
| return result; |
| } |
| |
| bool cmCPackWIXGenerator::IsLegalIdCharacter(char c) |
| { |
| return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || |
| (c >= 'A' && c <= 'Z') || c == '_' || c == '.'; |
| } |
| |
| void cmCPackWIXGenerator::CollectExtensions(std::string const& variableName, |
| extension_set_t& extensions) |
| { |
| const char* variableContent = GetOption(variableName.c_str()); |
| if (!variableContent) |
| return; |
| |
| std::vector<std::string> list; |
| cmSystemTools::ExpandListArgument(variableContent, list); |
| extensions.insert(list.begin(), list.end()); |
| } |
| |
| void cmCPackWIXGenerator::AddCustomFlags(std::string const& variableName, |
| std::ostream& stream) |
| { |
| const char* variableContent = GetOption(variableName.c_str()); |
| if (!variableContent) |
| return; |
| |
| std::vector<std::string> list; |
| cmSystemTools::ExpandListArgument(variableContent, list); |
| |
| for (std::string const& i : list) { |
| stream << " " << QuotePath(i); |
| } |
| } |
| |
| std::string cmCPackWIXGenerator::RelativePathWithoutComponentPrefix( |
| std::string const& path) |
| { |
| if (this->Components.empty()) { |
| return path; |
| } |
| |
| std::string::size_type pos = path.find('/'); |
| |
| return path.substr(pos + 1); |
| } |