diff --git a/Modules/CPackWIX.cmake b/Modules/CPackWIX.cmake index 3f0978de6..237c5bc53 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 0bc7e10ce..59a75c75b 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 1d7681b93..91701c2a6 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; } + + if(featureShortcuts.size()) + { + if(!CreateStartMenuShortcuts(component.Name, componentFeatureId, + featureShortcuts, fileDefinitions, featureDefinitions)) + { + return false; + } + } + } } - AddDirectoryAndFileDefinitons( - toplevel, "INSTALL_ROOT", - directoryDefinitions, fileDefinitions, featureDefinitions, - cpackPackageExecutablesList); - - 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,15 +531,153 @@ 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, + shortcut_map_t& shortcutMap) +{ + featureDefinitions.BeginElement("FeatureRef"); + featureDefinitions.AddAttribute("Id", featureId); + + std::vector 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 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) { - if(shortcutMap.empty()) - { - return true; - } + 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 481a07d45..84f68b623 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 214b8accf..c8a392299 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 0c7803ce4..670d4c0e9 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 b54925aff..ecfecdb4b 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"