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.
This commit is contained in:
Nils Gladitz 2013-11-19 21:38:09 +01:00
parent 1da77bf1ee
commit 7b390f75e8
7 changed files with 326 additions and 49 deletions

View File

@ -63,6 +63,14 @@
# #
# If set, this icon is used in place of the default icon. # 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 ``<UIRef>`` 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 # .. variable:: CPACK_WIX_UI_BANNER
# #
# The bitmap will appear at the top of all installer pages other than the # The bitmap will appear at the top of all installer pages other than the

View File

@ -39,6 +39,6 @@
<FeatureRef Id="ProductFeature"/> <FeatureRef Id="ProductFeature"/>
<UIRef Id="WixUI_InstallDir" /> <UIRef Id="$(var.CPACK_WIX_UI_REF)" />
</Product> </Product>
</Wix> </Wix>

View File

@ -198,6 +198,18 @@ bool cmCPackWIXGenerator::InitializeWiXConfiguration()
<< std::endl); << 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_EXTENSIONS", candleExtensions);
CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", candleExtensions); CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", candleExtensions);
@ -296,6 +308,7 @@ bool cmCPackWIXGenerator::CreateWiXVariablesIncludeFile()
SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER", SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER",
GetOption("CPACK_PACKAGE_NAME")); GetOption("CPACK_PACKAGE_NAME"));
CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER"); CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER");
CopyDefinition(includeFile, "CPACK_WIX_UI_REF");
return true; return true;
} }
@ -401,40 +414,77 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles()
featureDefinitions.BeginElement("Feature"); featureDefinitions.BeginElement("Feature");
featureDefinitions.AddAttribute("Id", "ProductFeature"); 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"); featureDefinitions.AddAttribute("Level", "1");
CreateFeatureHierarchy(featureDefinitions);
featureDefinitions.EndElement("Feature"); featureDefinitions.EndElement("Feature");
featureDefinitions.BeginElement("FeatureRef"); bool hasShortcuts = false;
featureDefinitions.AddAttribute("Id", "ProductFeature");
std::vector<std::string> cpackPackageExecutablesList; shortcut_map_t globalShortcuts;
const char *cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES"); if(Components.empty())
if(cpackPackageExecutables)
{ {
cmSystemTools::ExpandListArgument(cpackPackageExecutables, AddComponentsToFeature(toplevel, "ProductFeature",
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;
}
}
AddDirectoryAndFileDefinitons(
toplevel, "INSTALL_ROOT",
directoryDefinitions, fileDefinitions, featureDefinitions, directoryDefinitions, fileDefinitions, featureDefinitions,
cpackPackageExecutablesList); globalShortcuts);
if(globalShortcuts.size())
{
hasShortcuts = true;
}
}
else
{
for(std::map<std::string, cmCPackComponent>::const_iterator
i = Components.begin(); i != Components.end(); ++i)
{
cmCPackComponent const& component = i->second;
if(!CreateStartMenuShortcuts( std::string componentPath = toplevel;
directoryDefinitions, fileDefinitions, featureDefinitions)) 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())
{
hasShortcuts = true;
}
if(featureShortcuts.size())
{
if(!CreateStartMenuShortcuts(component.Name, componentFeatureId,
featureShortcuts, fileDefinitions, featureDefinitions))
{ {
return false; return false;
} }
}
}
}
if(hasShortcuts)
{
if(!CreateStartMenuShortcuts(std::string(), "ProductFeature",
globalShortcuts, fileDefinitions, featureDefinitions))
{
return false;
}
}
featureDefinitions.EndElement("FeatureRef");
featureDefinitions.EndElement("Fragment"); featureDefinitions.EndElement("Fragment");
fileDefinitions.EndElement("Fragment"); fileDefinitions.EndElement("Fragment");
@ -444,6 +494,12 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles()
} }
directoryDefinitions.EndElement("Directory"); directoryDefinitions.EndElement("Directory");
if(hasShortcuts)
{
CreateStartMenuFolder(directoryDefinitions);
}
directoryDefinitions.EndElement("Directory"); directoryDefinitions.EndElement("Directory");
directoryDefinitions.EndElement("Fragment"); directoryDefinitions.EndElement("Fragment");
@ -475,15 +531,153 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles()
return true; return true;
} }
bool cmCPackWIXGenerator::CreateStartMenuShortcuts( bool cmCPackWIXGenerator::CreateFeatureHierarchy(
cmWIXSourceWriter& featureDefinitions)
{
for(std::map<std::string, cmCPackComponentGroup>::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<std::string, cmCPackComponent>::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<cmCPackComponentGroup*>::const_iterator
i = group.Subgroups.begin(); i != group.Subgroups.end(); ++i)
{
if(!EmitFeatureForComponentGroup(featureDefinitions, **i))
{
return false;
}
}
for(std::vector<cmCPackComponent*>::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& directoryDefinitions,
cmWIXSourceWriter& fileDefinitions, cmWIXSourceWriter& fileDefinitions,
cmWIXSourceWriter& featureDefinitions,
shortcut_map_t& shortcutMap)
{
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;
}
}
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) cmWIXSourceWriter& featureDefinitions)
{ {
if(shortcutMap.empty()) featureDefinitions.BeginElement("FeatureRef");
{ featureDefinitions.AddAttribute("Id", featureId);
return true;
}
std::string cpackVendor; std::string cpackVendor;
if(!RequireOption("CPACK_PACKAGE_VENDOR", cpackVendor)) if(!RequireOption("CPACK_PACKAGE_VENDOR", cpackVendor))
@ -497,10 +691,19 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts(
return false; return false;
} }
std::string idSuffix;
if(!cpackComponentName.empty())
{
idSuffix += "_";
idSuffix += cpackComponentName;
}
std::string componentId = "CM_SHORTCUT" + idSuffix;
fileDefinitions.BeginElement("DirectoryRef"); fileDefinitions.BeginElement("DirectoryRef");
fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER"); fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER");
fileDefinitions.BeginElement("Component"); fileDefinitions.BeginElement("Component");
fileDefinitions.AddAttribute("Id", "SHORTCUT"); fileDefinitions.AddAttribute("Id", componentId);
fileDefinitions.AddAttribute("Guid", "*"); fileDefinitions.AddAttribute("Guid", "*");
for(shortcut_map_t::const_iterator for(shortcut_map_t::const_iterator
@ -522,10 +725,13 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts(
fileDefinitions.EndElement("Shortcut"); fileDefinitions.EndElement("Shortcut");
} }
if(cpackComponentName.empty())
{
CreateUninstallShortcut(cpackPackageName, fileDefinitions); CreateUninstallShortcut(cpackPackageName, fileDefinitions);
}
fileDefinitions.BeginElement("RemoveFolder"); fileDefinitions.BeginElement("RemoveFolder");
fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER"); fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER" + idSuffix);
fileDefinitions.AddAttribute("On", "uninstall"); fileDefinitions.AddAttribute("On", "uninstall");
fileDefinitions.EndElement("RemoveFolder"); fileDefinitions.EndElement("RemoveFolder");
@ -535,7 +741,15 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts(
fileDefinitions.BeginElement("RegistryValue"); fileDefinitions.BeginElement("RegistryValue");
fileDefinitions.AddAttribute("Root", "HKCU"); fileDefinitions.AddAttribute("Root", "HKCU");
fileDefinitions.AddAttribute("Key", registryKey); 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("Type", "integer");
fileDefinitions.AddAttribute("Value", "1"); fileDefinitions.AddAttribute("Value", "1");
fileDefinitions.AddAttribute("KeyPath", "yes"); fileDefinitions.AddAttribute("KeyPath", "yes");
@ -545,19 +759,10 @@ bool cmCPackWIXGenerator::CreateStartMenuShortcuts(
fileDefinitions.EndElement("DirectoryRef"); fileDefinitions.EndElement("DirectoryRef");
featureDefinitions.BeginElement("ComponentRef"); featureDefinitions.BeginElement("ComponentRef");
featureDefinitions.AddAttribute("Id", "SHORTCUT"); featureDefinitions.AddAttribute("Id", componentId);
featureDefinitions.EndElement("ComponentRef"); featureDefinitions.EndElement("ComponentRef");
directoryDefinitions.BeginElement("Directory"); featureDefinitions.EndElement("FeatureRef");
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");
return true; return true;
} }
@ -628,7 +833,8 @@ void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons(
cmWIXSourceWriter& directoryDefinitions, cmWIXSourceWriter& directoryDefinitions,
cmWIXSourceWriter& fileDefinitions, cmWIXSourceWriter& fileDefinitions,
cmWIXSourceWriter& featureDefinitions, cmWIXSourceWriter& featureDefinitions,
const std::vector<std::string>& packageExecutables) const std::vector<std::string>& packageExecutables,
shortcut_map_t& shortcutMap)
{ {
cmsys::Directory dir; cmsys::Directory dir;
dir.Load(topdir.c_str()); dir.Load(topdir.c_str());
@ -662,7 +868,8 @@ void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons(
directoryDefinitions, directoryDefinitions,
fileDefinitions, fileDefinitions,
featureDefinitions, featureDefinitions,
packageExecutables); packageExecutables,
shortcutMap);
directoryDefinitions.EndElement("Directory"); directoryDefinitions.EndElement("Directory");
} }
@ -922,3 +1129,18 @@ void cmCPackWIXGenerator::AddCustomFlags(
stream << " " << QuotePath(*i); 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");
}

View File

@ -56,7 +56,7 @@ protected:
virtual bool SupportsComponentInstallation() const virtual bool SupportsComponentInstallation() const
{ {
return false; return true;
} }
private: private:
@ -79,9 +79,30 @@ private:
bool CreateWiXSourceFiles(); 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& directoryDefinitions,
cmWIXSourceWriter& fileDefinitions, 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); cmWIXSourceWriter& featureDefinitions);
void CreateUninstallShortcut( void CreateUninstallShortcut(
@ -106,7 +127,8 @@ private:
cmWIXSourceWriter& directoryDefinitions, cmWIXSourceWriter& directoryDefinitions,
cmWIXSourceWriter& fileDefinitions, cmWIXSourceWriter& fileDefinitions,
cmWIXSourceWriter& featureDefinitions, cmWIXSourceWriter& featureDefinitions,
const std::vector<std::string>& pkgExecutables); const std::vector<std::string>& pkgExecutables,
shortcut_map_t& shortcutMap);
bool RequireOption(const std::string& name, std::string& value) const; bool RequireOption(const std::string& name, std::string& value) const;
@ -136,10 +158,11 @@ private:
void AddCustomFlags( void AddCustomFlags(
const std::string& variableName, std::ostream& stream); const std::string& variableName, std::ostream& stream);
void CreateStartMenuFolder(cmWIXSourceWriter& directoryDefinitions);
std::vector<std::string> wixSources; std::vector<std::string> wixSources;
id_map_t pathToIdMap; id_map_t pathToIdMap;
ambiguity_map_t idAmbiguityCounter; ambiguity_map_t idAmbiguityCounter;
shortcut_map_t shortcutMap;
extension_set_t candleExtensions; extension_set_t candleExtensions;
extension_set_t lightExtensions; extension_set_t lightExtensions;

View File

@ -123,6 +123,15 @@ void cmWIXSourceWriter::AddAttribute(
file << " " << key << "=\"" << EscapeAttributeValue(utf8) << '"'; 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) std::string cmWIXSourceWriter::WindowsCodepageToUtf8(const std::string& value)
{ {
if(value.empty()) if(value.empty())

View File

@ -40,6 +40,9 @@ public:
void AddAttribute( void AddAttribute(
const std::string& key, const std::string& value); 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); static std::string WindowsCodepageToUtf8(const std::string& value);
private: private:

View File

@ -14,11 +14,16 @@ install(TARGETS mylib
DESTINATION lib DESTINATION lib
COMPONENT libraries) COMPONENT libraries)
install(TARGETS my-libapp my-other-app install(TARGETS my-libapp
RUNTIME RUNTIME
DESTINATION bin DESTINATION bin
COMPONENT applications) COMPONENT applications)
install(TARGETS my-other-app
RUNTIME
DESTINATION bin
COMPONENT applications2)
install(FILES mylib.h "file with spaces.h" install(FILES mylib.h "file with spaces.h"
DESTINATION include DESTINATION include
COMPONENT headers) COMPONENT headers)
@ -56,11 +61,18 @@ cpack_add_component_group(Development
DESCRIPTION "All of the tools you'll ever need to develop software") DESCRIPTION "All of the tools you'll ever need to develop software")
cpack_add_component(applications cpack_add_component(applications
REQUIRED
DISPLAY_NAME "MyLib Application" DISPLAY_NAME "MyLib Application"
DESCRIPTION "An extremely useful application that makes use of MyLib" DESCRIPTION "An extremely useful application that makes use of MyLib"
GROUP Runtime GROUP Runtime
INSTALL_TYPES Full) 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 cpack_add_component(documentation
DISPLAY_NAME "MyLib Documentation" DISPLAY_NAME "MyLib Documentation"
DESCRIPTION "The extensive suite of MyLib Application documentation files" DESCRIPTION "The extensive suite of MyLib Application documentation files"