CPack Basics
CPack is CMake's packaging tool — it takes the install rules you defined with install() and wraps them into distributable packages. CPack supports over a dozen generators producing everything from tarballs to graphical installers.
cmake --install build --prefix /tmp/staging before running CPack.
Enabling CPack
Add CPack at the end of your top-level CMakeLists.txt (after all install() commands):
cmake_minimum_required(VERSION 3.21)
project(MyApp VERSION 2.3.1 LANGUAGES CXX)
# ... targets, install() rules ...
# CPack configuration (MUST come before include(CPack))
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VENDOR "My Company")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A high-performance data processing tool")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_PACKAGE_CONTACT "dev@mycompany.com")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
# Enable CPack (reads all CPACK_* variables)
include(CPack)
# Build the project first
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
# Create packages
cd build
cpack # Uses default generator(s)
cpack -G TGZ # Specific generator
cpack -G "DEB;RPM" # Multiple generators
cpack --config CPackSourceConfig.cmake # Source package
Core CPACK Variables
| Variable | Purpose | Example |
|---|---|---|
CPACK_GENERATOR | Default generator(s) | "TGZ;DEB" |
CPACK_PACKAGE_FILE_NAME | Output filename | "myapp-2.3.1-Linux-x86_64" |
CPACK_PACKAGING_INSTALL_PREFIX | Install prefix inside package | /usr |
CPACK_PACKAGE_ICON | Icon for installer | "${CMAKE_SOURCE_DIR}/icon.png" |
CPACK_STRIP_FILES | Strip debug symbols | TRUE |
CPACK_THREADS | Compression threads | 0 (all cores) |
Source Packages
CPack can create source archives for distribution:
# Source package configuration
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "myapp-${PROJECT_VERSION}-src")
set(CPACK_SOURCE_IGNORE_FILES
/\\.git/
/build.*/
/\\.vscode/
/\\.cache/
"\\\\.o$"
"\\\\.swp$"
)
# Generate source package
cd build
cpack --config CPackSourceConfig.cmake
# Result: myapp-2.3.1-src.tar.gz and myapp-2.3.1-src.zip
flowchart TD
A[CMakeLists.txt] -->|"install() rules"| B[Build Tree]
A -->|"CPACK_* variables"| C[CPack Configuration]
B --> D[cpack command]
C --> D
D --> E{Generator Selection}
E -->|TGZ/ZIP| F[Source/Binary Tarball]
E -->|DEB| G[Debian Package .deb]
E -->|RPM| H[Red Hat Package .rpm]
E -->|NSIS| I[Windows Installer .exe]
E -->|DMG| J[macOS Disk Image .dmg]
E -->|WIX| K[Windows MSI .msi]
Binary Packages
DEB Generator (Debian/Ubuntu)
The DEB generator creates packages for Debian-based distributions:
# DEB-specific configuration
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Your Name <you@example.com>")
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/you/myapp")
# Dependencies — critical for proper installation
set(CPACK_DEBIAN_PACKAGE_DEPENDS
"libc6 (>= 2.31), libstdc++6 (>= 10), libssl3 (>= 3.0)")
# Auto-detect shared library dependencies
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
# Package architecture (auto-detected if not set)
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
# Generate debug info package separately
set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON)
# Generate .deb package
cpack -G DEB
# Inspect the package
dpkg-deb --info myapp-2.3.1-Linux.deb
dpkg-deb --contents myapp-2.3.1-Linux.deb
# Install
sudo dpkg -i myapp-2.3.1-Linux.deb
# Or with dependency resolution
sudo apt install ./myapp-2.3.1-Linux.deb
RPM Generator (Fedora/RHEL)
The RPM generator creates packages for Red Hat-based distributions:
# RPM-specific configuration
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Tools")
set(CPACK_RPM_PACKAGE_URL "https://github.com/you/myapp")
set(CPACK_RPM_PACKAGE_DESCRIPTION "Detailed package description
spanning multiple lines for the RPM spec file.")
# Dependencies
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 3.0, zlib >= 1.2")
# Auto-detect dependencies from ELF binaries
set(CPACK_RPM_PACKAGE_AUTOREQ ON)
set(CPACK_RPM_PACKAGE_AUTOPROV ON)
# Post-install script
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/scripts/post-install.sh")
# Exclude certain paths from packaging
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
/usr/local
/usr/local/bin
/usr/local/lib
)
Build a Complete .deb Package
Create a minimal project that produces a proper Debian package:
cmake_minimum_required(VERSION 3.21)
project(HelloPkg VERSION 1.0.0 LANGUAGES CXX)
add_executable(hellopkg src/main.cpp)
install(TARGETS hellopkg RUNTIME DESTINATION bin)
install(FILES README.md DESTINATION share/doc/hellopkg)
set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Dev <dev@test.com>")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.31)")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
include(CPack)
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
cd build && cpack -G DEB
dpkg-deb --info hellopkg-1.0.0-Linux.deb
Platform Installers
NSIS Generator (Windows)
The NSIS generator creates professional Windows installers with UI wizards:
# NSIS-specific configuration
set(CPACK_NSIS_DISPLAY_NAME "My Application")
set(CPACK_NSIS_PACKAGE_NAME "MyApp")
set(CPACK_NSIS_HELP_LINK "https://myapp.example.com/help")
set(CPACK_NSIS_URL_INFO_ABOUT "https://myapp.example.com")
set(CPACK_NSIS_CONTACT "support@myapp.com")
# Start menu and desktop shortcuts
set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\MyApp.lnk' '$INSTDIR\\\\bin\\\\myapp.exe'")
set(CPACK_NSIS_DELETE_ICONS_EXTRA
"Delete '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\MyApp.lnk'")
# Add to PATH
set(CPACK_NSIS_MODIFY_PATH ON)
# Installer icon
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources/icon.ico")
set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/resources/icon.ico")
# Request admin privileges
set(CPACK_NSIS_EXECUTABLES_DIRECTORY "bin")
# Enable component selection in installer UI
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
DMG Generator (macOS)
The DMG generator creates macOS disk images:
# DMG-specific configuration
set(CPACK_DMG_VOLUME_NAME "MyApp ${PROJECT_VERSION}")
set(CPACK_DMG_FORMAT "UDZO") # Compressed disk image
set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_SOURCE_DIR}/packaging/DS_Store_setup.scpt")
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/packaging/dmg-background.png")
# Create symlink to /Applications in DMG
set(CPACK_DMG_SLA_USE_RESOURCE_FILE_LICENSE ON)
# Bundle configuration (for .app bundles)
set(CPACK_BUNDLE_NAME "MyApp")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/MyApp.icns")
set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/resources/Info.plist")
Component-Based Packaging
Component packaging lets users choose which parts to install — runtime, development headers, documentation, etc.
Defining Components
# Assign install rules to components
install(TARGETS myapp
RUNTIME DESTINATION bin
COMPONENT Runtime
)
install(TARGETS mylib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
COMPONENT Runtime
)
install(DIRECTORY include/
DESTINATION include
COMPONENT Development
FILES_MATCHING PATTERN "*.h"
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib.pc
DESTINATION lib/pkgconfig
COMPONENT Development
)
install(DIRECTORY docs/
DESTINATION share/doc/myapp
COMPONENT Documentation
)
# Register components with descriptions
cpack_add_component(Runtime
DISPLAY_NAME "Runtime Libraries"
DESCRIPTION "Core application and shared libraries"
REQUIRED
)
cpack_add_component(Development
DISPLAY_NAME "Development Files"
DESCRIPTION "Headers and CMake config for building against MyLib"
DEPENDS Runtime
)
cpack_add_component(Documentation
DISPLAY_NAME "Documentation"
DESCRIPTION "User guide and API reference"
DISABLED
)
Component Groups
include(CPackComponent)
# Define component groups
cpack_add_component_group(Core
DISPLAY_NAME "Core Components"
DESCRIPTION "Essential runtime and libraries"
EXPANDED
)
cpack_add_component_group(Extras
DISPLAY_NAME "Extra Components"
DESCRIPTION "Optional development and documentation files"
)
# Assign components to groups
cpack_add_component(Runtime GROUP Core)
cpack_add_component(Development GROUP Extras)
cpack_add_component(Documentation GROUP Extras)
# Enable component-based packaging
set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
# Options: ALL_COMPONENTS_IN_ONE, IGNORE, ONE_PER_GROUP
flowchart TD
A[install rules] --> B{COMPONENT keyword}
B --> C[Runtime Component]
B --> D[Development Component]
B --> E[Documentation Component]
C --> F[Core Group]
D --> G[Extras Group]
E --> G
F --> H{CPACK_COMPONENTS_GROUPING}
G --> H
H -->|ONE_PER_GROUP| I[myapp-core.deb]
H -->|ONE_PER_GROUP| J[myapp-extras.deb]
H -->|ALL_IN_ONE| K[myapp-complete.deb]
H -->|IGNORE| L[myapp-runtime.deb]
H -->|IGNORE| M[myapp-dev.deb]
H -->|IGNORE| N[myapp-doc.deb]
Generate Per-Component DEB Packages
Produce separate .deb files for runtime and development:
set(CPACK_DEB_COMPONENT_INSTALL ON)
set(CPACK_COMPONENTS_GROUPING IGNORE)
# Each component gets its own .deb:
# myapp-1.0.0-Linux-Runtime.deb
# myapp-1.0.0-Linux-Development.deb
# myapp-1.0.0-Linux-Documentation.deb
# Per-component DEB metadata
set(CPACK_DEBIAN_RUNTIME_PACKAGE_DEPENDS "libc6 (>= 2.31)")
set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_DEPENDS "myapp-runtime (= ${PROJECT_VERSION})")
cd build && cpack -G DEB
ls *.deb
# myapp-1.0.0-Linux-Runtime.deb
# myapp-1.0.0-Linux-Development.deb
Relocatable Packages
A relocatable package can be installed to any prefix without hardcoded paths breaking. Achieve this by using relative RPATHs and generator expressions:
# Set RPATH for installed binaries to find bundled libraries
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# Or on macOS:
set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
# Ensure RPATH is set at install time (not stripped)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_SKIP_BUILD_RPATH FALSE)
# For CPack: packaging-specific prefix
set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/myapp")
# TGZ packages are inherently relocatable
set(CPACK_GENERATOR "TGZ")
set(CPACK_PACKAGE_FILE_NAME "myapp-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-portable")
Create a Self-Contained Portable Package
# Build relocatable package
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_RPATH='$ORIGIN/../lib'
cmake --build build
cd build && cpack -G TGZ
# Extract and run from anywhere
tar xzf myapp-2.3.1-Linux-portable.tar.gz -C /tmp
/tmp/myapp-2.3.1-Linux-portable/bin/myapp
# Works! Libraries found via relative RPATH
Conclusion & Next Steps
CPack transforms your install rules into professional distributable packages across all major platforms. Key takeaways:
include(CPack)must come after all CPACK_* variables and install() rules- Use
CPACK_DEBIAN_PACKAGE_SHLIBDEPSfor automatic dependency detection on Debian - Component packaging with
cpack_add_component()enables fine-grained distribution - NSIS (Windows) and DMG (macOS) require their respective platforms to build
- Relative RPATHs (
$ORIGIN/../lib) make packages truly relocatable