Installing and Packaging
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 MyLib
FILE "${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 MyLib
EXPORT MyLibTargets
ARCHIVE DESTINATION lib # Static libs (.a/.lib)
LIBRARY DESTINATION lib # Shared libs (.so/.dll)
RUNTIME DESTINATION bin # Executables/DLLs on Windows
INCLUDES DESTINATION include
)
install(
DIRECTORY include/ DESTINATION include
FILES LICENSE DESTINATION doc
)
# Install exported targets for downstream projects
install(
EXPORT MyLibTargets
FILE MyLibConfig.cmake
DESTINATION 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.cmake
INSTALL_DESTINATION lib/cmake/MyLib
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake
VERSION 1.2.3
COMPATIBILITY SameMajorVersion # Accepts 1.x.x
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake
DESTINATION lib/cmake/MyLib
)
Template File MyLibConfig.cmake.in
:
@PACKAGE_INIT@ # CMake-provided initialization macro
include("${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 MyLib
EXPORT MyLibTargets
ARCHIVE DESTINATION lib COMPONENT development
LIBRARY DESTINATION lib COMPONENT runtime
INCLUDES DESTINATION include COMPONENT development
)
install(DIRECTORY include/
DESTINATION include
COMPONENT development
)
install(FILES LICENSE
DESTINATION 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 PROPERTIES
VERSION 1.2.3 # Full version
SOVERSION 1 # API compatibility version
)
This generates:
libMyLib.so.1.2.3
(real binary)libMyLib.so.1
→libMyLib.so.1.2.3
libMyLib.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(runtime
DISPLAY_NAME "Runtime"
DESCRIPTION "Runtime components"
REQUIRED
)
cpack_add_component(development
DISPLAY_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
VERSION
properties match between targets andConfigVersion.cmake
. - Symlink Handling: On Windows, shared libraries don’t use symlinks; focus on
VERSION
/SOVERSION
for 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). TheEXPORT
keyword requiresinstall(EXPORT)
(D). Exported targets do not auto-resolve dependencies (C is false).
A
CMAKE_INSTALL_INCLUDEDIR
defaults toinclude/
(A).CMAKE_INSTALL_PREFIX
is the root (B).BINDIR
andLIBDIR
are for binaries/libraries (C/D incorrect).
B, C
install(DIRECTORY ...)
copies headers (B).PUBLIC_HEADER
property 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
COMPONENT
ininstall()
assigns targets (B).CPACK_DEB_COMPONENT_INSTALL
enables 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/SOVERSION
controls symlink names (B).NAMELINK_SKIP
skips symlinks ©. Manual symlinks (A) are error-prone.CMAKE_INSTALL_SO_NO_EXE
is unrelated (D).
A, C
RUNTIME_DEPENDENCIES
bundles dependencies (A).BundleUtilities
is for macOS ©.CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
is 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).ExactVersion
is not a valid argument (A).COMPATIBLE_VERSION_INTERNAL
is internal ©.
A, B
Config.cmake
must be inCMAKE_PREFIX_PATH
(A).find_dependency()
handles transitive deps (B).VERSION
inproject()
doesn’t enforce compatibility ©.CMAKE_MODULE_PATH
is 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.cmake
andMathLibConfigVersion.cmake
Answer:
include(GNUInstallDirs)
# Install targets
install(TARGETS MathLib
ARCHIVE 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 MathLibTargets
FILE MathLibTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathLib
NAMESPACE MathLib::
)
# Config files
include(CMakePackageConfigHelpers)
configure_package_config_file(
MathLibConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/MathLibConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathLib
)
write_basic_package_version_file(
MathLibConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/MathLibConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/MathLibConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathLib
)
Explanation:
- GNUInstallDirs ensures cross-platform compatible install paths
ARCHIVE/LIBRARY/RUNTIME
destinations handle different target typesINCLUDES
propagates 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.service
DESTINATION /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_GENERATOR
selects DEB package formatCPACK_DEBIAN_FILE_NAME
uses default naming conventionCPACK_DEBIAN_PACKAGE_DEPENDS
specifies runtime dependencies- Systemd service file installed to standard location
CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
adds 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
runtime
anddevelopment
components - Versioned shared library with symlinks:
libmath.so.1.2.3
(real)libmath.so.1
(major version)libmath.so
(latest)
- Generate
MathLibConfigVersion.cmake
that:- Rejects versions < 2.0.0
- Compatible with same major version
- Component requirements:
development
requiresruntime
docs
optional component
Answer:
# Versioned library setup
set_target_properties(MathLib PROPERTIES
VERSION 1.2.3
SOVERSION 1
OUTPUT_NAME "math"
)
# Component definitions
cpack_add_component(runtime DISPLAY_NAME "Runtime")
cpack_add_component(development
DISPLAY_NAME "Development"
DEPENDS runtime
)
cpack_add_component(docs
DISPLAY_NAME "Documentation"
DISABLED
)
# Installation rules
install(TARGETS MathLib
EXPORT MathLibTargets
RUNTIME DESTINATION bin COMPONENT runtime
LIBRARY DESTINATION lib COMPONENT runtime
ARCHIVE DESTINATION lib COMPONENT development
INCLUDES DESTINATION include/MathLib
)
install(EXPORT MathLibTargets
FILE MathLibTargets.cmake
DESTINATION lib/cmake/MathLib
COMPONENT development
NAMESPACE MathLib::
)
# Version config
write_basic_package_version_file(
MathLibConfigVersion.cmake
VERSION 2.0.0
COMPATIBILITY 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.cmake
INSTALL_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
COMPONENT
arguments - 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