Lesson 23 · Build & ship
Create a CMake package
Turn your install into a real package. The Config.cmake file, the version file, and the find_package story from the consumer's side.
In lesson 17 you used find_package(fmt), and it worked because fmt’s authors shipped a fmtConfig.cmake. This lesson shows you how to ship the same for your library. When you are done, your users write exactly two lines.
The goal
user's CMakeLists.txtfind_package(calc 1.0 REQUIRED) target_link_libraries(myapp PRIVATE calc::calc)
For that to work, the installed library must include, next to itself:
lib/cmake/calc/
├── calcConfig.cmake # "here is the calc::calc target"
└── calcConfigVersion.cmake # "which versions of calc am I?"
The commands
Building on lesson 22, the full packaging block in your CMakeLists.txt:
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
# 1. Install targets and export set (from lesson 22).
install(TARGETS calc
EXPORT calcTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(FILES include/calc/calc.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/calc
)
# 2. One export call. The generated file doubles as the package Config file.
install(EXPORT calcTargets
FILE calcConfig.cmake # exactly this name
NAMESPACE calc::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/calc
)
# 3. The version file next to it, so find_package(calc 1.0) can check versions.
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/calcConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/calcConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/calc
)
find_package(calc) searches for calcConfig.cmake. If a version is requested, it first consults the sibling version file, which claims compatibility or not. Name the file exactly <Name>Config.cmake (or <name>-config.cmake) and everything just works.
COMPATIBILITY, which to pick
AnyNewerVersion. The default choice. Any newer version is compatible. Perfect for libraries with a stable API.SameMajorVersion. Only same-major versions (semantic versioning).SameMinorVersion. Only same-minor.ExactVersion. Deprecated in CMake 4.4. It is almost never what you want. If your library can only ever work with an exact version, that is a packaging smell.
Why
The version file exists so consumers can state requirements,
find_package(calc 2.0), and CMake can refuse mismatches before loading your Config file. A clear error instead of a broken build.
The consumer side, what you just enabled
cmake --install build --prefix /opt/calc
cmake -S . -B build -DCMAKE_PREFIX_PATH=/opt/calc
find_package(calc 1.0 REQUIRED)
target_link_libraries(myapp PRIVATE calc::calc)
If the header include dirs were declared PUBLIC on the calc target, the consumer gets them for free (lesson 9). That is the whole chain closing.
Pitfall
A consumer error like “
find_package(calc)could not findcalcConfig.cmake” after a correct install usually meansCMAKE_PREFIX_PATHpoints at the wrong prefix. CMake looks forlib/cmake/calc/under the prefixes you give it. Install to/opt/calc, then-DCMAKE_PREFIX_PATH=/opt/calc, not/opt/calc/lib/cmake/calc.
Tip
CMakePackageConfigHelpersis bundled with CMake. No downloads. And keepproject(calc VERSION 1.2.3 ...)(lesson 5) as the single source of the version.write_basic_package_version_filereads${PROJECT_VERSION}from it, so you bump one number and everything follows.
Read the docs