Lesson 24 · Build & ship

Packaging with CPack

Package your installed files into a tarball, zip or .deb with CPack. The one-module recipe that turns install() into distributable artifacts.

You have made your project installable (lesson 22). CPack is the module that turns “install” into a distributable artifact, a .tar.gz, .zip, .deb, or .exe installer, so users do not have to run cmake --install themselves.

The minimum

CMakeLists.txt
include(CPack)

That is the whole setup. CPack packages everything your install() rules install (lesson 22). Then:

# Build, install rules exist, then:
cmake --build build --target package
build/
└── calc-1.2.0-Linux.tar.gz     # everything install() would install

Why

CPack reuses your install() rules. One description of “what ships” powers both cmake --install and every package format. That is the design. You never write a packaging manifest separately.

Choosing the format

include(CPack)

set(CPACK_GENERATOR "TGZ")        # .tar.gz
# or: set(CPACK_GENERATOR "ZIP")  # cross-platform zip
# or: set(CPACK_GENERATOR "DEB")  # Debian package (needs dpkg tools)

Pick per platform with a sensible default:

if(WIN32)
    set(CPACK_GENERATOR "ZIP")
else()
    set(CPACK_GENERATOR "TGZ")
endif()

Metadata that matters

set(CPACK_PACKAGE_NAME "calc")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})     # from project(VERSION ...)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A small math library")
set(CPACK_PACKAGE_VENDOR "Example Org")

# DEB specifics
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "you@example.com")

Two rules that prevent silent breakage

  1. install() destinations must be relative paths. From the CPack docs, if an install destination is absolute, “installed files are ignored by CPack”. The file exists when you run cmake --install, but is silently missing from the package. Always use relative destinations (bin, ${CMAKE_INSTALL_LIBDIR}, never /usr/bin).
  2. Do not forget headers. Packages ship headers, libraries and binaries. Lesson 22’s install(FILES ...) is what puts calc.h in your package. Without it, users get a library they cannot compile against.

Presets and the command line

CMakePresets.json
"packagePresets": [ { "name": "release", "configurePreset": "release", "generators": ["TGZ"] } ]
cpack --preset release

Or with --config for multi-config generators, cpack -C Release.

Note

With Makefile and Ninja generators, include(CPack) also adds the package and package_source build targets. That is what cmake --build build --target package used above. package_source archives your source tree, handy for source releases.

Pitfall

CPack artifacts embed the package name and version. A wrong PROJECT_VERSION or a missing CPACK_PACKAGE_NAME shows up in weird filenames like calc-1.2.0-Linux.tar.gz when you expected something else. Keep the version in project(...) (single source of truth, lesson 5) and let CPACK_PACKAGE_VERSION ${PROJECT_VERSION} copy it.

Tip

Open the produced archive and compare it against your install() list at least once. CPack is “install plus compress”, and 90 percent of packaging bugs are install-rule bugs wearing a different name.

Read the docs