CMake:Component Install With CPack
Component-Based Installers with CPack
CPack builds binary installers for a variety of platforms using CMake's existing installation infrastructure (see the list of supported CPack generators). Augmented by a set of CPack-specific macros, a program built with CMake can easily be distributed via a user-friendly installer.
By default, CPack's installers consider all of the files installed by a project as a single, monolithic unit: either the whole set of files is installed, or none of the files are installed. However, with many projects it makes sense for the installation to be subdivided into distinct, user-selectable components: some users may want to install only the comand-line tools for a project, while other users might want the GUI or the header files.
This document describes how to configure CPack to generate component-based installers that allow users to select the set of project components that they wish to install.
Principles of CPack Component Packaging
The principles of CPack component packaging is based on differents ways to group things together in order to define component content.
Rules
There are some basic rules about components:
- An object cannot belong to several COMPONENT
- Each COMPONENT may only belong to a single component GROUP
- Each COMPONENT may included in several INSTALL_TYPE
- The name of each COMPONENT should be unique (and different from Unspecified which is a reserved component name)
- The name of a component GROUP should be unique and cannot be the same as the name of a COMPONENT
- A component GROUP may have a PARENT_GROUP which is a component GROUP
- A component does not have to belong to any component GROUP
- There may be no component GROUP at all (using component grouping is not mandatory)
- There may be no component at all (using component install is not mandatory)
- Not all CPack generators honor component packaging some of them just ignore the component specification
Specificying components and groups
First for each install rule like this one:
INSTALL(... [COMPONENT component] ...)
the COMPONENT option should be specified with the name of the chosen component. This means that all objects (TARGETS, FILES, ...) concerned by this install rule will belong to the specified component.
If some INSTALL rule does not specify the COMPONENT CMake will create a COMPONENT named Unspecified which contains the target of every such rules.
After that grouping and depencies may be specified by two ways:
FIXME TO BE CONTINUED (see CPack.cmake embedded documentation)
CPack Generator specific behavior
CPack comes with several binary Generators: NSIS, PackageMaker, TGZ, ZIP, RPM, DEB... Depending on the capacity of the specific generator the component packaging will produce:
- For component-aware generators:
- a single "component-aware" package file : NSIS, PackageMaker
- possibly multiple package files: ArchiveGenerator (TBZ2, TGZ, STGZ, TZ, ZIP), RPM
- For non-component-aware generators:
- a single MONOLITHIC package file just as if no component were specified: DEB
Controlling packaging of component-aware generators
Forcing MONOLITHIC installers
The component aware generators may be forced to ignore component if one set:
set(CPACK_MONOLITHIC_INSTALL 1)
If this variable is set at CMake time (inside a CMakeLists.txt) then all generators will be producing a single a.k.a. MONOLITHIC package file. Since CMake/CPack 2.8.4 the CPACK_MONOLITHIC_INSTALL var may be handled at CPack time if set inside CPACK_PROJECT_CONFIG_FILE.
Enabling Component Packaging
Some component-aware generators have backward-compatible behavior which makes them NOT to generate component install. In fact, this is currently the case for ArchiveGenerator and RPM. In that particular case if you want to change this default non-component-aware behavior you may set the specific variable CPACK_<GENNAME>_COMPONENT_INSTALL to ON. For example for enabling component for the RPM generator you'll have to:
set(CPACK_RPM_COMPONENT_INSTALL ON)
Controlling Differents Ways of packaging components
For CPack generators which generates several packages the default behavior is to generate one package per COMPONENT GROUP. However, one can modify this default behavior by using:
- Single package file containing all COMPONENTS (ignore COMPONENT GROUP and single file):
set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1)
or
set(CPACK_COMPONENTS_GROUPING "ALL_COMPONENTS_IN_ONE")
- One package per COMPONENT (ignore COMPONENT GROUP but several files):
set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
or
set(CPACK_COMPONENTS_GROUPING "IGNORE")
Step-by-step example Component-Based Installers
In this document, we will develop a simple installer for a simple library that has three components: a library binary, a sample application, and a C++ header file. When we have finished, these resulting installers will looks like the customizable installers below, shown for Mac OS X and Windows:
Prerequisites
To begin, you should be able to build graphical installers using CPack either on Windows (using the NullSoft Installation System, NSIS) or Mac OS X (using PackageMaker). Also, as of the time of this writing, the extensions to CPack required to build component-based installers are only available since CMake 2.6.1 (NSIS and PackageMaker) or CMake 2.8.3 (Archive Generator). Component support for other CPack generator should come sooner or later, check the release notes.
As our primary example, we will use a simple library called "MyLib" that builds a single library (`mylib`) and an application based on that library (`mylibapp`). The file File:ComponentExampleStart.zip contains the skeleton of such a library, with the following build and installation commands in `CMakeLists.txt`:
cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR) project(MyLib)
add_library(mylib mylib.cpp)
add_executable(mylibapp mylibapp.cpp) target_link_libraries(mylibapp mylib)
install(TARGETS mylib ARCHIVE DESTINATION lib) install(TARGETS mylibapp RUNTIME DESTINATION bin) install(FILES mylib.h DESTINATION include)
You should be able to configure, build, and (optionally) install this project using CMake with its library, application, and header file.
Building Binary Installers with CPack
To build binary installers with CPack, first add the following to the end of `CMakeLists.txt`:
set(CPACK_PACKAGE_NAME "MyLib") set(CPACK_PACKAGE_VENDOR "CMake.org") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MyLib - CPack Component Installation Example") set(CPACK_PACKAGE_VERSION "1.0.0") 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")
# This must always be last! include(CPack)
More information about CPack and its configuration macros can be found here, but this boilerplate suffices to enable the packaging target in a CMake build. From here, makefile users can invove `make package` to build a binary installer (e.g., on Mac OS X) and Visual Studio users can build the PACKAGE target.
Throughout this tutorial, we will be setting more `CPACK_` macros in `CMakeLists.txt` to communicate with CPack. It is very important that the SET commands for these macros come before the include of the `CPack` module!
Identifying Components
The first step in building a component-based installation is to identify the set of installable components. In our example library, we have decided on three components: the library binary, the application, and the header file. This decision is arbitrary and project-specific, but be sure to identify the components that correspond to units of functionality important to your user rather than basing the components on the internal structure of your program.
For each of these components, we need to identify which installed files are part of the component. For each INSTALL command in `CMakeLists.txt`, add an appropriate COMPONENT argument stating which component the installed files will be associated with:
install(TARGETS mylib ARCHIVE DESTINATION lib COMPONENT libraries) install(TARGETS mylibapp RUNTIME DESTINATION bin COMPONENT applications) install(FILES mylib.h DESTINATION include COMPONENT headers)
Note that the COMPONENT argument to the INSTALL command is not new; it has been a part of CMake's INSTALL command to allow installation of only part of a project. If you are using any of the older installation commands (INSTALL_TARGETS, INSTALL_FILES , etc.), you will need to convert them to INSTALL commands to add the COMPONENT argument.
Finally, notify CPack of the names of all of the components in your project by setting the CPACK_COMPONENTS_ALL variables:
set(CPACK_COMPONENTS_ALL applications libraries headers)
At this point, you can build a component-based installer with CPack that will allow one to independently install the applications, libraries, and headers of MyLib.
Component Descriptions
If you have built a binary installer at this point, you may have noted that the names of the actual components in the installer are not very descriptive: they just say "applications", "libraries", or "headers", as in the component names. We can improve on these names by setting several additional CPack variables:
set(CPACK_COMPONENT_APPLICATIONS_DISPLAY_NAME "MyLib Application") set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C++ Headers")
Any macro prefixed with CPACK_COMPONENT_${COMPNAME}, where ${COMPNAME} is the uppercase name of a component, is used to set a particular property of that component in the installer. Here, we set the DISPLAY_NAME property of each of our components, so that we get human-readable names. These names will be listed in the selection box rather than the internal component names "applications", "libraries", "headers".
There are several other properties associated with components, including the ability to make a component hidden, required, or disabled by default, to provide additional descriptive information, etc. We will encounter some of these other properties later; see the macro reference for a complete list.
Of particular note is the DESCRIPTION property, which provides some descriptive text for the component. This descriptive text will show up in a separate "description" box in the installer, and will be updated either when the user's mouse hovers over the name of the corresponding component (Windows) or when the user clicks on a component (Mac OS X). Here, we add a description for each of our components:
set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION "An extremely useful application that makes use of MyLib") set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "Static libraries used to build programs with MyLib") set(CPACK_COMPONENT_HEADERS_DESCRIPTION "C/C++ header files for use with MyLib")
Generally, descriptions should provide enough information for the user to make a decision whether to include the component, but should not themselves be more than a few lines long (because the "Description" box in the installers tends to be small).
Intercomponent Dependencies
With most projects, the various components are not completely independent. For example, an application component may depend on the shared libraries in another component to execute properly, such that installing the application component without the corresponding shared libraries would result in an unusable installation. CPack allows you to express the dependencies between components, so that a component will only be installed if all of the other components it depends on are also installed.
To illustrate component dependencies, we will place a simple restriction on our component-based installer. Since we do not provide source code in our installer, the C++ header files we distribute can only actually be used if the user also installs the library binary to link her program against. Thus, the "headers" component depends on the availability of the "libraries" component. We can express this notion by setting the DEPENDS property for the HEADERS component as such:
set(CPACK_COMPONENT_HEADERS_DEPENDS libraries)
The DEPENDS property for a component is actually at list, and as such a component can depend on several other components. By expressing all of the component dependencies in this manner, you can ensure that users will not be able to select an incomplete set of components at installation time.
Grouping Components
When the number of components in your project grows large, you may need to provide additional organization for the list of components. To help with this organization, CPack includes the notion of component groups. A component group is, simply, a way to provide a name for a group of related components. Within the user interface, a component group has its own name, and underneath that group come all of the names of the components within the group. Users will have the option to (de-)select installation of all components in the group with a single click, or expand the group to select individual components.
We will expand our example by categorizing its three components, "applications", "libraries", and "headers", into "Runtime" and "Development" groups. We place a component into a group by setting the GROUP property of the component to the name of the group as follows:
set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime") set(CPACK_COMPONENT_LIBRARIES_GROUP "Development") set(CPACK_COMPONENT_HEADERS_GROUP "Development")
Like components, component groups have various properties that can be customized, including the DISPLAY_NAME and DESCRIPTION. To customization a component group, set the appropriate CPack macro with the prefix CPACK_COMPONENT_GROUP_${GROUPNAME}, where ${GROUPNAME} is the uppercase name of the group to modify. For example, the following code adds a description to the "Development" group:
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION "All of the tools you'll ever need to develop software")
Once you have customized the component groups to your liking, rebuild the binary installer to see the new organization: the MyLib application will show up under the new "Runtime" group, while the MyLib library and C++ header will show up under the new "Development" group. One can easily turn on/off all of the components within a group using the installer GUI. Some additional customizations of component groups are possible; please see the macro reference for a complete list.
Installation Types (NSIS Only)
When a project contains a large number of components, it is common for a Windows installer to provide pre-selected sets of components based on specific user needs. For example, a user wanting to develop software against a library will want one set of components, while an end user might use an entirely different set. CPack supports this notion of pre-selected component sets via installation types. An installation type is, simply, a set of components. When the user selects an installation type, exactly that set of components is selected---then the user is permitted to further customize the installation as desired.
For our simple example, we will create only two installation types: a "Full" installation type, which contains all of the components, and a "Developer" installation type, which includes only the libraries and headers. To do so, we first tell CPack which installation types we're using:
set(CPACK_ALL_INSTALL_TYPES Full Developer)
Like components and component groups, installation types have some properties (e.g., DISPLAY_NAME), which can be set via variables with prefix CPACK_INSTALL_TYPE_${INSTNAME}, where ${INSTNAME} is the uppercase name of the installation type. See the macro reference for additional information.
Next, we set the INSTALL_TYPES property of each component to state which installation types will include that component:
set(CPACK_COMPONENT_LIBRARIES_INSTALL_TYPES Developer Full) set(CPACK_COMPONENT_HEADERS_INSTALL_TYPES Developer Full) set(CPACK_COMPONENT_APPLICATIONS_INSTALL_TYPES Full)
Components can be in any number of installation types. If you now rebuild the Windows installer, the components page will contain a combo box that allows you to select the installation type and therefore its corresponding set of components.
Next Steps
From here, you should be able to turn your existing CPack-generated binary installers into component-based installers to provide your users with more-flexible installation options. The complete example constructed by this tutorial is available as File:ComponentExample.zip. For a more advanced example of a component-based installer build with CPack, please visit the Boost-CMake project.
Macro Reference
In this reference, ${COMPNAME} refers to a component, ${GROUPNAME} refers to a component group, and ${INSTNAME} refers to an installation type, all of which are uppercase.
Variable Name | Description |
---|---|
CPACK_COMPONENTS_ALL | A list containing the names of all components that should be installed. The presence of this macro indicates that CPack should build a component-based installer. Files associated with any components not listed here or any installation commands not associated with any component will not be installed. |
CPACK_COMPONENT_${COMPNAME}_DISPLAY_NAME | The displayed name of the component ${COMPNAME}, used in graphical installers to display the component name. This value can be any string. |
CPACK_COMPONENT_${COMPNAME}_DESCRIPTION | An extended description of the component ${COMPNAME}, used in graphical installers to give the user additional information about the component. Descriptions can span multiple lines using "\n" as the line separator. |
CPACK_COMPONENT_${COMPNAME}_HIDDEN | Flag that indicates that this component will be hidden in the graphical installer, and therefore cannot be selected or installed. Only available with NSIS. |
CPACK_COMPONENT_${COMPNAME}_REQUIRED | Flag that indicates that this component is required, and therefore will always be installed. It will be visible in the graphical installer, but it cannot be unselected. |
CPACK_COMPONENT_${COMPNAME}_DISABLED | Flag that indicates that this component should be disabled (unselected) by default. The user is free to select this component for installation. |
CPACK_COMPONENT_${COMPNAME}_DEPENDS | Lists the components on which this component depends. If this component is selected, then each of the components listed must also be selected. |
CPACK_COMPONENT_${COMPNAME}_GROUP | Names the component group of which this component is a part. If not provided, the component will be a standalone component, not part of any component group. |
CPACK_COMPONENT_${COMPNAME}_INSTALL_TYPES | Lists the installation types of which this component is a part. When one of these installations types is selected, this component will automatically be selected. Only available with NSIS. |
CPACK_COMPONENT_GROUP_${GROUPNAME}_DISPLAY_NAME | The displayed name of the component group ${GROUPNAME}, used in graphical installers to display the component group name. This value can be any string. |
CPACK_COMPONENT_GROUP_${GROUPNAME}_DESCRIPTION | An extended description of the component group ${GROUPNAME}, used in graphical installers to give the user additional information about the components contained within this group. Descriptions can span multiple lines using "\n" as the line separator. |
CPACK_COMPONENT_GROUP_${GROUPNAME}_BOLD_TITLE | Flag indicating whether the group title should be in bold. Only available with NSIS. |
CPACK_COMPONENT_GROUP_${GROUPNAME}_EXPANDED | Flag indicating whether the group should start out "expanded", showing its components. Otherwise only the group name itself will be shown until the user clicks on the group. Only available with NSIS. |
CPACK_INSTALL_TYPE_${INSTNAME}_DISPLAY_NAME | The displayed name of the installation type. This value can be any string. |
Ideas for Future Development
- Add support for other binary installer generators, such as RPM and Deb. The installer will then create a set of RPM or Deb files, with appropriate dependencies. A key question here is granularity: RPMs and Debs tend to have a coarser granularity than graphical installers.
- CPack RPM generator component support: This is true that RPM has coarser granularity, however one may build 1 RPM by component and embbed appropriate dependencies between each generated RPM. For building several RPM at once you may either generate a spec file for each component or generate a single spec file which may contain sub-packages [[1]]. --Erk 04:36, 28 August 2008 (EDT)
- Graphviz output of components and their dependencies?