Table of Contents

  1. CPack Basics
  2. Binary Packages
  3. Platform Installers
  4. Component-Based Packaging
  5. Relocatable Packages
  6. Conclusion & Next Steps
Back to CMake Mastery Series

Part 22: Packaging with CPack

June 4, 2026 Wasil Zafar 35 min read

Create professional DEB, RPM, NSIS, and DMG packages from your CMake project. Learn component-based packaging, metadata configuration, and building relocatable installers for cross-platform distribution.

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.

Key Insight: CPack reads the same install() rules that cmake --install uses. If your installation works correctly, CPack will package it correctly. Always verify your install tree with 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

VariablePurposeExample
CPACK_GENERATORDefault generator(s)"TGZ;DEB"
CPACK_PACKAGE_FILE_NAMEOutput filename"myapp-2.3.1-Linux-x86_64"
CPACK_PACKAGING_INSTALL_PREFIXInstall prefix inside package/usr
CPACK_PACKAGE_ICONIcon for installer"${CMAKE_SOURCE_DIR}/icon.png"
CPACK_STRIP_FILESStrip debug symbolsTRUE
CPACK_THREADSCompression threads0 (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
CPack Package Generation Flow
        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
)
Hands-On DEB Package Creation
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
DEB dpkg packaging

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")
Platform Note: NSIS packages can only be built on Windows (requires NSIS installed), and DMG packages can only be built on macOS. Use CI/CD with platform-specific jobs to build packages for all platforms.

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
Component Packaging Architecture
        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]
    
Hands-On Component Packaging
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
components DEB split packages

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")
Hands-On Portable Tarball
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
RPATH relocatable portable

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_SHLIBDEPS for 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
Official Reference: See the full CPack module documentation and individual generator pages for all available variables.