From 7b390f75e82ad79f6759656c52825c89e5c92902 Mon Sep 17 00:00:00 2001 From: Nils Gladitz Date: Tue, 19 Nov 2013 21:38:09 +0100 Subject: CPackWiX: add CPack component support Creates a hierarchy of WiX features from CPack components and component groups. Switch to the FeatureTree UI in case components have been defined. Handles the component REQUIRE and HIDDEN options and the component group EXPANDED option. --- Modules/CPackWIX.cmake | 8 + Modules/WIX.template.in | 2 +- Source/CPack/WiX/cmCPackWIXGenerator.cxx | 306 ++++++++++++++++++++++++++----- Source/CPack/WiX/cmCPackWIXGenerator.h | 31 +++- Source/CPack/WiX/cmWIXSourceWriter.cxx | 9 + Source/CPack/WiX/cmWIXSourceWriter.h | 3 + Tests/CPackWiXGenerator/CMakeLists.txt | 14 +- 7 files changed, 325 insertions(+), 48 deletions(-) diff --git a/Modules/CPackWIX.cmake b/Modules/CPackWIX.cmake index 3f0978d..237c5bc 100644 --- a/Modules/CPackWIX.cmake +++ b/Modules/CPackWIX.cmake @@ -63,6 +63,14 @@ # # If set, this icon is used in place of the default icon. # +# .. variable:: CPACK_WIX_UI_REF +# +# This variable allows you to override the Id of the ```` element +# in the WiX template. +# +# The default is ``WixUI_InstallDir`` in case no CPack components have +# been defined and ``WixUI_FeatureTree`` otherwise. +# # .. variable:: CPACK_WIX_UI_BANNER # # The bitmap will appear at the top of all installer pages other than the diff --git a/Modules/WIX.template.in b/Modules/WIX.template.in index 0bc7e10..59a75c7 100644 --- a/Modules/WIX.template.in +++ b/Modules/WIX.template.in @@ -39,6 +39,6 @@ - + diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index 1d7681b..91701c2 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -198,6 +198,18 @@ bool cmCPackWIXGenerator::InitializeWiXConfiguration() << std::endl); } + if(GetOption("CPACK_WIX_UI_REF") == 0) + { + std::string defaultRef = "WixUI_InstallDir"; + + if(Components.size()) + { + defaultRef = "WixUI_FeatureTree"; + } + + SetOption("CPACK_WIX_UI_REF", defaultRef.c_str()); + } + CollectExtensions("CPACK_WIX_EXTENSIONS", candleExtensions); CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", candleExtensions); @@ -296,6 +308,7 @@ bool cmCPackWIXGenerator::CreateWiXVariablesIncludeFile() SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER", GetOption("CPACK_PACKAGE_NAME")); CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER"); + CopyDefinition(includeFile, "CPACK_WIX_UI_REF"); return true; } @@ -401,40 +414,77 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() featureDefinitions.BeginElement("Feature"); featureDefinitions.AddAttribute("Id", "ProductFeature"); - featureDefinitions.AddAttribute("Title", Name); + featureDefinitions.AddAttribute("Display", "expand"); + featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT"); + + std::string cpackPackageName; + if(!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) + { + return false; + } + featureDefinitions.AddAttribute("Title", cpackPackageName); + featureDefinitions.AddAttribute("Level", "1"); + + CreateFeatureHierarchy(featureDefinitions); + featureDefinitions.EndElement("Feature"); - featureDefinitions.BeginElement("FeatureRef"); - featureDefinitions.AddAttribute("Id", "ProductFeature"); + bool hasShortcuts = false; - std::vector cpackPackageExecutablesList; - const char *cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES"); - if(cpackPackageExecutables) + shortcut_map_t globalShortcuts; + if(Components.empty()) { - cmSystemTools::ExpandListArgument(cpackPackageExecutables, - cpackPackageExecutablesList); - if(cpackPackageExecutablesList.size() % 2 != 0 ) + AddComponentsToFeature(toplevel, "ProductFeature", + directoryDefinitions, fileDefinitions, featureDefinitions, + globalShortcuts); + if(globalShortcuts.size()) + { + hasShortcuts = true; + } + } + else + { + for(std::map::const_iterator + i = Components.begin(); i != Components.end(); ++i) + { + cmCPackComponent const& component = i->second; + + std::string componentPath = toplevel; + componentPath += "/"; + componentPath += component.Name; + + std::string componentFeatureId = "CM_C_" + component.Name; + + shortcut_map_t featureShortcuts; + AddComponentsToFeature(componentPath, componentFeatureId, + directoryDefinitions, fileDefinitions, + featureDefinitions, featureShortcuts); + if(featureShortcuts.size()) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "CPACK_PACKAGE_EXECUTABLES should contain pairs of and " - "." << std::endl); - return false; + hasShortcuts = true; } - } - AddDirectoryAndFileDefinitons( - toplevel, "INSTALL_ROOT", - directoryDefinitions, fileDefinitions, featureDefinitions, - cpackPackageExecutablesList); + if(featureShortcuts.size()) + { + if(!CreateStartMenuShortcuts(component.Name, componentFeatureId, + featureShortcuts, fileDefinitions, featureDefinitions)) + { + return false; + } + } + } + } - if(!CreateStartMenuShortcuts( - directoryDefinitions, fileDefinitions, featureDefinitions)) + if(hasShortcuts) { + if(!CreateStartMenuShortcuts(std::string(), "ProductFeature", + globalShortcuts, fileDefinitions, featureDefinitions)) + { return false; + } } - featureDefinitions.EndElement("FeatureRef"); featureDefinitions.EndElement("Fragment"); fileDefinitions.EndElement("Fragment"); @@ -444,6 +494,12 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() } directoryDefinitions.EndElement("Directory"); + + if(hasShortcuts) + { + CreateStartMenuFolder(directoryDefinitions); + } + directoryDefinitions.EndElement("Directory"); directoryDefinitions.EndElement("Fragment"); @@ -475,16 +531,154 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() return true; } -bool cmCPackWIXGenerator::CreateStartMenuShortcuts( +bool cmCPackWIXGenerator::CreateFeatureHierarchy( + cmWIXSourceWriter& featureDefinitions) +{ + for(std::map::const_iterator + i = ComponentGroups.begin(); i != ComponentGroups.end(); ++i) + { + cmCPackComponentGroup const& group = i->second; + if(group.ParentGroup == 0) + { + if(!EmitFeatureForComponentGroup(featureDefinitions, group)) + { + return false; + } + } + } + + for(std::map::const_iterator + i = Components.begin(); i != Components.end(); ++i) + { + cmCPackComponent const& component = i->second; + + if(!component.Group) + { + if(!EmitFeatureForComponent(featureDefinitions, component)) + { + return false; + } + } + } + + return true; +} + +bool cmCPackWIXGenerator::EmitFeatureForComponentGroup( + cmWIXSourceWriter& featureDefinitions, + cmCPackComponentGroup const& group) +{ + featureDefinitions.BeginElement("Feature"); + featureDefinitions.AddAttribute("Id", "CM_G_" + group.Name); + + if(group.IsExpandedByDefault) + { + featureDefinitions.AddAttribute("Display", "expand"); + } + + featureDefinitions.AddAttributeUnlessEmpty( + "Title", group.DisplayName); + + featureDefinitions.AddAttributeUnlessEmpty( + "Description", group.Description); + + for(std::vector::const_iterator + i = group.Subgroups.begin(); i != group.Subgroups.end(); ++i) + { + if(!EmitFeatureForComponentGroup(featureDefinitions, **i)) + { + return false; + } + } + + for(std::vector::const_iterator + i = group.Components.begin(); i != group.Components.end(); ++i) + { + if(!EmitFeatureForComponent(featureDefinitions, **i)) + { + return false; + } + } + + featureDefinitions.EndElement("Feature"); + + return true; +} + +bool cmCPackWIXGenerator::EmitFeatureForComponent( + cmWIXSourceWriter& featureDefinitions, + cmCPackComponent const& component) +{ + featureDefinitions.BeginElement("Feature"); + featureDefinitions.AddAttribute("Id", "CM_C_" + component.Name); + + featureDefinitions.AddAttributeUnlessEmpty( + "Title", component.DisplayName); + + featureDefinitions.AddAttributeUnlessEmpty( + "Description", component.Description); + + if(component.IsRequired) + { + featureDefinitions.AddAttribute("Absent", "disallow"); + } + + if(component.IsHidden) + { + featureDefinitions.AddAttribute("Display", "hidden"); + } + + featureDefinitions.EndElement("Feature"); + + return true; +} + +bool cmCPackWIXGenerator::AddComponentsToFeature( + std::string const& rootPath, + std::string const& featureId, cmWIXSourceWriter& directoryDefinitions, cmWIXSourceWriter& fileDefinitions, - cmWIXSourceWriter& featureDefinitions) + cmWIXSourceWriter& featureDefinitions, + shortcut_map_t& shortcutMap) { - if(shortcutMap.empty()) + featureDefinitions.BeginElement("FeatureRef"); + featureDefinitions.AddAttribute("Id", featureId); + + std::vector cpackPackageExecutablesList; + const char *cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES"); + if(cpackPackageExecutables) { - return true; + cmSystemTools::ExpandListArgument(cpackPackageExecutables, + cpackPackageExecutablesList); + if(cpackPackageExecutablesList.size() % 2 != 0 ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_EXECUTABLES should contain pairs of and " + "." << std::endl); + return false; + } } + AddDirectoryAndFileDefinitons( + rootPath, "INSTALL_ROOT", + directoryDefinitions, fileDefinitions, featureDefinitions, + cpackPackageExecutablesList, shortcutMap); + + featureDefinitions.EndElement("FeatureRef"); + + return true; +} + +bool cmCPackWIXGenerator::CreateStartMenuShortcuts( + std::string const& cpackComponentName, + std::string const& featureId, + shortcut_map_t& shortcutMap, + cmWIXSourceWriter& fileDefinitions, + cmWIXSourceWriter& featureDefinitions) +{ + featureDefinitions.BeginElement("FeatureRef"); + featureDefinitions.AddAttribute("Id", featureId); + std::string cpackVendor; if(!RequireOption("CPACK_PACKAGE_VENDOR", cpackVendor)) { @@ -497,10 +691,19 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts( return false; } + std::string idSuffix; + if(!cpackComponentName.empty()) + { + idSuffix += "_"; + idSuffix += cpackComponentName; + } + + std::string componentId = "CM_SHORTCUT" + idSuffix; + fileDefinitions.BeginElement("DirectoryRef"); fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER"); fileDefinitions.BeginElement("Component"); - fileDefinitions.AddAttribute("Id", "SHORTCUT"); + fileDefinitions.AddAttribute("Id", componentId); fileDefinitions.AddAttribute("Guid", "*"); for(shortcut_map_t::const_iterator @@ -522,10 +725,13 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts( fileDefinitions.EndElement("Shortcut"); } - CreateUninstallShortcut(cpackPackageName, fileDefinitions); + if(cpackComponentName.empty()) + { + CreateUninstallShortcut(cpackPackageName, fileDefinitions); + } fileDefinitions.BeginElement("RemoveFolder"); - fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER"); + fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER" + idSuffix); fileDefinitions.AddAttribute("On", "uninstall"); fileDefinitions.EndElement("RemoveFolder"); @@ -535,7 +741,15 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts( fileDefinitions.BeginElement("RegistryValue"); fileDefinitions.AddAttribute("Root", "HKCU"); fileDefinitions.AddAttribute("Key", registryKey); - fileDefinitions.AddAttribute("Name", "installed"); + + std::string valueName; + if(!cpackComponentName.empty()) + { + valueName = cpackComponentName + "_"; + } + valueName += "installed"; + + fileDefinitions.AddAttribute("Name", valueName); fileDefinitions.AddAttribute("Type", "integer"); fileDefinitions.AddAttribute("Value", "1"); fileDefinitions.AddAttribute("KeyPath", "yes"); @@ -545,19 +759,10 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts( fileDefinitions.EndElement("DirectoryRef"); featureDefinitions.BeginElement("ComponentRef"); - featureDefinitions.AddAttribute("Id", "SHORTCUT"); + featureDefinitions.AddAttribute("Id", componentId); featureDefinitions.EndElement("ComponentRef"); - directoryDefinitions.BeginElement("Directory"); - directoryDefinitions.AddAttribute("Id", "ProgramMenuFolder"); - - directoryDefinitions.BeginElement("Directory"); - directoryDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER"); - const char *startMenuFolder = GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER"); - directoryDefinitions.AddAttribute("Name", startMenuFolder); - directoryDefinitions.EndElement("Directory"); - - directoryDefinitions.EndElement("Directory"); + featureDefinitions.EndElement("FeatureRef"); return true; } @@ -628,7 +833,8 @@ void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons( cmWIXSourceWriter& directoryDefinitions, cmWIXSourceWriter& fileDefinitions, cmWIXSourceWriter& featureDefinitions, - const std::vector& packageExecutables) + const std::vector& packageExecutables, + shortcut_map_t& shortcutMap) { cmsys::Directory dir; dir.Load(topdir.c_str()); @@ -662,7 +868,8 @@ void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons( directoryDefinitions, fileDefinitions, featureDefinitions, - packageExecutables); + packageExecutables, + shortcutMap); directoryDefinitions.EndElement("Directory"); } @@ -922,3 +1129,18 @@ void cmCPackWIXGenerator::AddCustomFlags( stream << " " << QuotePath(*i); } } + +void cmCPackWIXGenerator::CreateStartMenuFolder( + cmWIXSourceWriter& directoryDefinitions) +{ + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", "ProgramMenuFolder"); + + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER"); + const char *startMenuFolder = GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER"); + directoryDefinitions.AddAttribute("Name", startMenuFolder); + directoryDefinitions.EndElement("Directory"); + + directoryDefinitions.EndElement("Directory"); +} diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h index 481a07d..84f68b6 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.h +++ b/Source/CPack/WiX/cmCPackWIXGenerator.h @@ -56,7 +56,7 @@ protected: virtual bool SupportsComponentInstallation() const { - return false; + return true; } private: @@ -79,9 +79,30 @@ private: bool CreateWiXSourceFiles(); - bool CreateStartMenuShortcuts( + bool CreateFeatureHierarchy( + cmWIXSourceWriter& featureDefinitions); + + bool EmitFeatureForComponentGroup( + cmWIXSourceWriter& featureDefinitions, + cmCPackComponentGroup const& group); + + bool EmitFeatureForComponent( + cmWIXSourceWriter& featureDefinitions, + cmCPackComponent const& component); + + bool AddComponentsToFeature( + std::string const& rootPath, + std::string const& featureId, cmWIXSourceWriter& directoryDefinitions, cmWIXSourceWriter& fileDefinitions, + cmWIXSourceWriter& featureDefinitions, + shortcut_map_t& shortcutMap); + + bool CreateStartMenuShortcuts( + std::string const& cpackComponentName, + std::string const& featureId, + shortcut_map_t& shortcutMap, + cmWIXSourceWriter& fileDefinitions, cmWIXSourceWriter& featureDefinitions); void CreateUninstallShortcut( @@ -106,7 +127,8 @@ private: cmWIXSourceWriter& directoryDefinitions, cmWIXSourceWriter& fileDefinitions, cmWIXSourceWriter& featureDefinitions, - const std::vector& pkgExecutables); + const std::vector& pkgExecutables, + shortcut_map_t& shortcutMap); bool RequireOption(const std::string& name, std::string& value) const; @@ -136,10 +158,11 @@ private: void AddCustomFlags( const std::string& variableName, std::ostream& stream); + void CreateStartMenuFolder(cmWIXSourceWriter& directoryDefinitions); + std::vector wixSources; id_map_t pathToIdMap; ambiguity_map_t idAmbiguityCounter; - shortcut_map_t shortcutMap; extension_set_t candleExtensions; extension_set_t lightExtensions; diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx index 214b8ac..c8a3922 100644 --- a/Source/CPack/WiX/cmWIXSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx @@ -123,6 +123,15 @@ void cmWIXSourceWriter::AddAttribute( file << " " << key << "=\"" << EscapeAttributeValue(utf8) << '"'; } +void cmWIXSourceWriter::AddAttributeUnlessEmpty( + const std::string& key, const std::string& value) +{ + if(value.size()) + { + AddAttribute(key, value); + } +} + std::string cmWIXSourceWriter::WindowsCodepageToUtf8(const std::string& value) { if(value.empty()) diff --git a/Source/CPack/WiX/cmWIXSourceWriter.h b/Source/CPack/WiX/cmWIXSourceWriter.h index 0c7803c..670d4c0 100644 --- a/Source/CPack/WiX/cmWIXSourceWriter.h +++ b/Source/CPack/WiX/cmWIXSourceWriter.h @@ -40,6 +40,9 @@ public: void AddAttribute( const std::string& key, const std::string& value); + void AddAttributeUnlessEmpty( + const std::string& key, const std::string& value); + static std::string WindowsCodepageToUtf8(const std::string& value); private: diff --git a/Tests/CPackWiXGenerator/CMakeLists.txt b/Tests/CPackWiXGenerator/CMakeLists.txt index b54925a..ecfecdb 100644 --- a/Tests/CPackWiXGenerator/CMakeLists.txt +++ b/Tests/CPackWiXGenerator/CMakeLists.txt @@ -14,11 +14,16 @@ install(TARGETS mylib DESTINATION lib COMPONENT libraries) -install(TARGETS my-libapp my-other-app +install(TARGETS my-libapp RUNTIME DESTINATION bin COMPONENT applications) +install(TARGETS my-other-app + RUNTIME + DESTINATION bin + COMPONENT applications2) + install(FILES mylib.h "file with spaces.h" DESTINATION include COMPONENT headers) @@ -56,11 +61,18 @@ cpack_add_component_group(Development DESCRIPTION "All of the tools you'll ever need to develop software") cpack_add_component(applications + REQUIRED DISPLAY_NAME "MyLib Application" DESCRIPTION "An extremely useful application that makes use of MyLib" GROUP Runtime INSTALL_TYPES Full) +cpack_add_component(applications2 + DISPLAY_NAME "MyLib Extra Application" + DESCRIPTION "Another 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" -- cgit v0.12