Installing and Packaging
- Chapter 14: Installing and Packaging
- 1. Exporting Targets Without Installation
- 2. Installing Projects System-Wide
- 3. Creating Reusable Packages
- 4. Component-Based Installation
- 5. Symbolic Links for Shared Libraries
- 6. Packaging with CPack
- Critical Takeaways
- Common Pitfalls
- Multiple Choice Questions
- Answers & Explanations
- Build Challenges
- Medium Question 1: Basic Installation and Exporting Targets
- Medium Question 2: CPack Configuration for DEB Packages
- Hard Question: Component-Based Installation with Versioned Libraries
Chapter 14: Installing and Packaging
1. Exporting Targets Without Installation
Key Concept: Allow other CMake projects to use your library without installing it system-wide.
Mechanism: Use export() to generate a <Project>Targets.cmake file for dependency resolution.
Code Example:
add_library(MyLib STATIC mylib.cpp)
target_include_directories(MyLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)# Export targets to a file
export(TARGETS MyLibFILE "${CMAKE_CURRENT_BINARY_DIR}/MyLibTargets.cmake"NAMESPACE MyNamespace::
)
This generates MyLibTargets.cmake, allowing other projects to include it via include() and link with MyNamespace::MyLib.
2. Installing Projects System-Wide
Key Concept: Install built artifacts (executables, libraries, headers) to standard system paths.
Critical Commands:
install(TARGETS): Install build targets.install(FILES|DIRECTORY): Install headers/docs.
Code Example:
install(TARGETS MyLibEXPORT MyLibTargetsARCHIVE DESTINATION lib # Static libs (.a/.lib)LIBRARY DESTINATION lib # Shared libs (.so/.dll)RUNTIME DESTINATION bin # Executables/DLLs on WindowsINCLUDES DESTINATION include
)install(DIRECTORY include/ DESTINATION includeFILES LICENSE DESTINATION doc
)# Install exported targets for downstream projects
install(EXPORT MyLibTargetsFILE MyLibConfig.cmakeDESTINATION lib/cmake/MyLib
)
Platform-Specific Paths:
- Unix:
/usr/local/(defaultCMAKE_INSTALL_PREFIX) - Windows:
C:/Program Files/
3. Creating Reusable Packages
Goal: Generate <Package>Config.cmake for find_package() compatibility.
Tools:
configure_package_config_file(): Generates config file with correct paths.write_basic_package_version_file(): Handles version compatibility.
Code Example:
include(CMakePackageConfigHelpers)configure_package_config_file(MyLibConfig.cmake.in${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmakeINSTALL_DESTINATION lib/cmake/MyLib
)write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmakeVERSION 1.2.3COMPATIBILITY SameMajorVersion # Accepts 1.x.x
)install(FILES${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmakeDESTINATION lib/cmake/MyLib
)
Template File MyLibConfig.cmake.in:
@PACKAGE_INIT@ # CMake-provided initialization macroinclude("${CMAKE_CURRENT_LIST_DIR}/MyLibTargets.cmake")
check_required_components(MyLib)
4. Component-Based Installation
Use Case: Split installation into logical components (e.g., runtime, development, docs).
Implementation: Add COMPONENT to install() commands.
Code Example:
install(TARGETS MyLibEXPORT MyLibTargetsARCHIVE DESTINATION lib COMPONENT developmentLIBRARY DESTINATION lib COMPONENT runtimeINCLUDES DESTINATION include COMPONENT development
)install(DIRECTORY include/ DESTINATION include COMPONENT development
)install(FILES LICENSEDESTINATION doc COMPONENT docs
)
Component-Specific Packaging (with CPack):
cpack_add_component(runtime DISPLAY_NAME "Runtime")
cpack_add_component(development DISPLAY_NAME "Development")
cpack_add_component(docs DISPLAY_NAME "Documentation")
5. Symbolic Links for Shared Libraries
Unix-Specific: Handle library versioning via symlinks (e.g., libfoo.so.1 → libfoo.so.1.2.3).
CMake Support: Use SOVERSION and VERSION target properties.
Code Example:
set_target_properties(MyLib PROPERTIESVERSION 1.2.3 # Full versionSOVERSION 1 # API compatibility version
)
This generates:
libMyLib.so.1.2.3(real binary)libMyLib.so.1→libMyLib.so.1.2.3libMyLib.so→libMyLib.so.1
6. Packaging with CPack
Goal: Generate platform-specific packages (DEB, RPM, ZIP, etc.).
Configuration:
set(CPACK_PACKAGE_NAME "MyLib")
set(CPACK_PACKAGE_VERSION "1.2.3")
set(CPACK_PACKAGE_DESCRIPTION "A sample library")# Component-aware packaging
set(CPACK_DEB_COMPONENT_INSTALL YES) # For Debian
include(CPack)
Generate Packages:
cmake --build build_dir --target install # Install artifacts first
cpack -G DEB # Generate Debian package
Component-Specific Variables:
cpack_add_component(runtimeDISPLAY_NAME "Runtime"DESCRIPTION "Runtime components"REQUIRED
)
cpack_add_component(developmentDISPLAY_NAME "Development"DEPENDS runtime
)
Critical Takeaways
- Exporting enables dependency resolution between projects without system-wide installation.
- Installation requires specifying destinations for different target types (executables, libs, headers).
- Config Files (
<Package>Config.cmake) make your library discoverable viafind_package(). - Components allow granular control over installation/packaging (e.g., separate dev and runtime files).
- CPack automates cross-platform package generation but requires careful configuration for components.
Common Pitfalls
- Missing Namespaces: Always use namespaces (
MyNamespace::) in exported targets to avoid conflicts. - Path Hardcoding: Use
CMAKE_INSTALL_PREFIX, not absolute paths, for portability. - Version Mismatch: Ensure
VERSIONproperties match between targets andConfigVersion.cmake. - Symlink Handling: On Windows, shared libraries don’t use symlinks; focus on
VERSION/SOVERSIONfor Unix.
Multiple Choice Questions
1. Which statements about exporting CMake targets without installation are correct?
A. export(TARGETS ...) writes target properties to a file for external projects.
B. install(TARGETS ... EXPORT) generates a <PackageName>Targets.cmake file.
C. Exported targets automatically resolve transitive dependencies.
D. The EXPORT keyword in install() requires a separate install(EXPORT ...) call.
2. Which variables define default installation paths for public headers on Unix-like systems?
A. CMAKE_INSTALL_INCLUDEDIR
B. CMAKE_INSTALL_PREFIX
C. CMAKE_INSTALL_BINDIR
D. CMAKE_INSTALL_LIBDIR
3. What are valid methods for installing public headers?
A. Use target_include_directories() with the PUBLIC keyword.
B. Use install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}).
C. Use set_target_properties(... PUBLIC_HEADER ...).
D. Use file(COPY include/ DESTINATION ...) followed by install(FILES ...).
4. Which are required steps to create a relocatable package with CMake?
A. Use absolute paths in target_include_directories().
B. Use configure_package_config_file() with @PACKAGE_INIT@.
C. Avoid CMAKE_INSTALL_PREFIX in find_dependency() calls.
D. Generate a <PackageName>ConfigVersion.cmake file.
5. What correctly describes component-based packaging in CMake?
A. Components are defined using cpack_add_component().
B. install(TARGETS ... COMPONENT runtime) assigns targets to a component.
C. find_package(... COMPONENTS runtime) filters installed components.
D. CPACK_DEB_COMPONENT_INSTALL enables component-aware DEB packages.
6. Which CPack generators support component-based packaging?
A. ZIP
B. DEB
C. NSIS
D. RPM
7. How to manage versioned shared library symbolic links during installation?
A. Use install(CODE "execute_process(...)") to create symlinks manually.
B. Use set_target_properties(... VERSION ... SOVERSION ...).
C. Use install(TARGETS ... LIBRARY DESTINATION ... NAMELINK_SKIP).
D. Use CMAKE_INSTALL_SO_NO_EXE to disable symlink creation.
8. Which statements about runtime dependencies installation are correct?
A. install(TARGETS ... RUNTIME_DEPENDENCIES) bundles all DLLs on Windows.
B. CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS specifies additional runtime libraries.
C. BundleUtilities is required for macOS .app bundles.
D. WIX CPack generator automatically resolves runtime dependencies.
9. Which are valid use cases for write_basic_package_version_file()?
A. Enforce package version compatibility using ExactVersion.
B. Generate a <PackageName>ConfigVersion.cmake file.
C. Compare versions using COMPATIBLE_VERSION_INTERNAL.
D. Define custom version-check logic with macros.
10. What ensures correct behavior when using find_package() for a custom package?
A. The package’s Config.cmake file must be in CMAKE_PREFIX_PATH.
B. find_dependency() must be used for transitive dependencies.
C. The VERSION field in project() defines package compatibility.
D. CMAKE_MODULE_PATH must include the package’s CMake/ directory.
Answers & Explanations
-
A, B, D
export(TARGETS)writes targets to a file (A).install(TARGETS ... EXPORT)triggers export file generation (B). TheEXPORTkeyword requiresinstall(EXPORT)(D). Exported targets do not auto-resolve dependencies (C is false).
-
A
CMAKE_INSTALL_INCLUDEDIRdefaults toinclude/(A).CMAKE_INSTALL_PREFIXis the root (B).BINDIRandLIBDIRare for binaries/libraries (C/D incorrect).
-
B, C
install(DIRECTORY ...)copies headers (B).PUBLIC_HEADERproperty installs headers viainstall(TARGETS)©.target_include_directories()doesn’t install files (A).file(COPY)lacks integration withinstall()(D is manual, not idiomatic).
-
B, C, D
configure_package_config_file()handles relative paths (B). Avoid absolute paths ©. Version files ensure compatibility (D). Absolute paths break relocatability (A is wrong).
-
B, D
COMPONENTininstall()assigns targets (B).CPACK_DEB_COMPONENT_INSTALLenables DEB components (D). Components are defined viainstall(... COMPONENT)(A is false).find_package(... COMPONENTS)checks availability (C is correct but not a step in creation).
-
B, D
- DEB (B) and RPM (D) support components. ZIP/NSIS (A/C) have limited/no component support.
-
B, C
VERSION/SOVERSIONcontrols symlink names (B).NAMELINK_SKIPskips symlinks ©. Manual symlinks (A) are error-prone.CMAKE_INSTALL_SO_NO_EXEis unrelated (D).
-
A, C
RUNTIME_DEPENDENCIESbundles dependencies (A).BundleUtilitiesis for macOS ©.CMAKE_INSTALL_SYSTEM_RUNTIME_LIBSis not a standard variable (B). WIX doesn’t auto-resolve (D).
-
B, D
write_basic_package_version_file()generates version files (B). Custom logic requires macros (D).ExactVersionis not a valid argument (A).COMPATIBLE_VERSION_INTERNALis internal ©.
-
A, B
Config.cmakemust be inCMAKE_PREFIX_PATH(A).find_dependency()handles transitive deps (B).VERSIONinproject()doesn’t enforce compatibility ©.CMAKE_MODULE_PATHis forFind*.cmake, notConfig.cmake(D).
Build Challenges
Medium Question 1: Basic Installation and Exporting Targets
Scenario:
You’re developing a C++ library MathLib with the following structure:
MathLib/
├── include/MathLib.h
├── src/Calculator.cpp
└── apps/Main.cpp
Create CMake installation rules to:
- Install the shared library to standard system locations
- Install public headers to
include/MathLib - Install the executable to
bin - Export targets for
find_package()usage - Generate both
MathLibConfig.cmakeandMathLibConfigVersion.cmake
Answer:
include(GNUInstallDirs)# Install targets
install(TARGETS MathLibARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/MathLib
)install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/MathLib)
install(TARGETS MathApp DESTINATION ${CMAKE_INSTALL_BINDIR})# Export targets
install(EXPORT MathLibTargetsFILE MathLibTargets.cmakeDESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathLibNAMESPACE MathLib::
)# Config files
include(CMakePackageConfigHelpers)
configure_package_config_file(MathLibConfig.cmake.in${CMAKE_CURRENT_BINARY_DIR}/MathLibConfig.cmakeINSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathLib
)write_basic_package_version_file(MathLibConfigVersion.cmakeVERSION ${PROJECT_VERSION}COMPATIBILITY SameMajorVersion
)install(FILES${CMAKE_CURRENT_BINARY_DIR}/MathLibConfig.cmake${CMAKE_CURRENT_BINARY_DIR}/MathLibConfigVersion.cmakeDESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathLib
)
Explanation:
- GNUInstallDirs ensures cross-platform compatible install paths
ARCHIVE/LIBRARY/RUNTIMEdestinations handle different target typesINCLUDESpropagates include directories to dependent targetsinstall(EXPORT)creates target export files with proper namespacing- configure_package_config_file generates config file with relocatable paths
- write_basic_package_version_file handles version compatibility checks
- Final install rules place all config files in standard CMake package location
Medium Question 2: CPack Configuration for DEB Packages
Scenario:
Configure CPack to generate DEB packages with:
- Package name
mathlib-<version>.deb - Maintainer info “Dev Team dev@mathlib.org”
- Dependency on libboost-all-dev (>= 1.65)
- Systemd service file installation
- Post-install script that runs
ldconfig
Answer:
set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Dev Team <dev@mathlib.org>")
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-all-dev (>= 1.65)")install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mathlib.serviceDESTINATION /lib/systemd/system
)set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"${CMAKE_CURRENT_BINARY_DIR}/postinst"
)# Create postinst script
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/postinst CONTENT "#!/bin/sh\nldconfig\n")include(CPack)
Explanation:
CPACK_GENERATORselects DEB package formatCPACK_DEBIAN_FILE_NAMEuses default naming conventionCPACK_DEBIAN_PACKAGE_DEPENDSspecifies runtime dependencies- Systemd service file installed to standard location
CPACK_DEBIAN_PACKAGE_CONTROL_EXTRAadds post-install script- file(GENERATE) creates required ldconfig script
- Final
include(CPack)activates packaging support
Hard Question: Component-Based Installation with Versioned Libraries
Scenario:
Create an installation with:
- Separate
runtimeanddevelopmentcomponents - Versioned shared library with symlinks:
libmath.so.1.2.3(real)libmath.so.1(major version)libmath.so(latest)
- Generate
MathLibConfigVersion.cmakethat:- Rejects versions < 2.0.0
- Compatible with same major version
- Component requirements:
developmentrequiresruntimedocsoptional component
Answer:
# Versioned library setup
set_target_properties(MathLib PROPERTIESVERSION 1.2.3SOVERSION 1OUTPUT_NAME "math"
)# Component definitions
cpack_add_component(runtime DISPLAY_NAME "Runtime")
cpack_add_component(developmentDISPLAY_NAME "Development"DEPENDS runtime
)
cpack_add_component(docsDISPLAY_NAME "Documentation"DISABLED
)# Installation rules
install(TARGETS MathLibEXPORT MathLibTargetsRUNTIME DESTINATION bin COMPONENT runtimeLIBRARY DESTINATION lib COMPONENT runtimeARCHIVE DESTINATION lib COMPONENT developmentINCLUDES DESTINATION include/MathLib
)install(EXPORT MathLibTargetsFILE MathLibTargets.cmakeDESTINATION lib/cmake/MathLibCOMPONENT developmentNAMESPACE MathLib::
)# Version config
write_basic_package_version_file(MathLibConfigVersion.cmakeVERSION 2.0.0COMPATIBILITY SameMajorVersion
)# Compatibility check
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/MathLibConfig.cmake.in"if(NOT MathLib_FIND_VERSION VERSION_GREATER_EQUAL 2.0.0)\n"" message(FATAL_ERROR \"Requires at least version 2.0.0\")\n""endif()\n""include(\${CMAKE_CURRENT_LIST_DIR}/MathLibTargets.cmake)"
)configure_package_config_file(MathLibConfig.cmake.in${CMAKE_CURRENT_BINARY_DIR}/MathLibConfig.cmakeINSTALL_DESTINATION lib/cmake/MathLib
)include(CPack)
Explanation:
- VERSION/SOVERSION creates versioned library files and symlinks
- cpack_add_component defines component hierarchy and dependencies
- Split installation using
COMPONENTarguments - write_basic_package_version_file enforces major version compatibility
- Custom config file adds minimum version check
- Library symlinks are automatically managed via SOVERSION property
- Documentation component is disabled by default
- Development component includes archive (static lib) and CMake configs
- Runtime component contains shared library and runtime dependencies
Key Concepts Reinforcement:
- Component-Based Installation: Separates logical parts of the package using CPack components
- Versioned Libraries: Uses target properties to manage shared library versioning and symlinks
- Config Version Validation: Ensures compatibility through version checks in config files
- Cross-Component Dependencies: Manages inter-component requirements via DEPENDS
- Package Configuration: Combines target exports with custom validation logic
- Symlink Management: Automatic handling through CMake target properties rather than manual commands
- Conditional Installation: Disabled components (like docs) can be enabled during package creation
