Lesson 22 · Build & ship
Installing and exporting
Make your project installable. install() targets and headers to standard prefixes, and the export files that make targets reusable.
Building your project is half the job. Installing it is how other people and other projects use it. This lesson makes your project installable in about ten lines, and prepares lesson 23, shipping a package that find_package can find.
What install means
Installing copies your built artifacts, libraries, executables, headers, into a standard prefix (/usr/local, C:\Program Files, or a folder you choose) in a well-known layout:
/usr/local/
├── bin/
│ └── myapp
├── lib/
│ ├── libcalc.a
│ └── cmake/calc/ # package files (lesson 23)
└── include/
└── calc/
└── calc.h
cmake --install build
# or, into a staging folder:
cmake --install build --prefix /tmp/stage
The install() commands
CMakeLists.txt# Standard install locations. bin/, lib/, include/ for this platform. include(GNUInstallDirs) # 1. The targets, grouped in an export set named "calcTargets". install(TARGETS calc EXPORT calcTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # .a / .lib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # .so / .dylib RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # .dll / executables ) # 2. The public headers. install(FILES include/calc/calc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/calc ) # 3. Write the export file, CalcTargets.cmake, with all targets as # IMPORTED targets, installed next to the library. install(EXPORT calcTargets FILE calcTargets.cmake NAMESPACE calc:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/calc )
Note
install(TARGETS ... EXPORT name)groups your targets into a named set.install(EXPORT name ...)generates a.cmakefile that recreates those targets as imported targets,calc::calc, wherever that file is included. That pairing is the heart of the whole packaging story.
Why GNUInstallDirs
include(GNUInstallDirs)
It defines the platform-correct destinations, lib/ versus lib64/ on some Linux distributions, bin/ on all. Instead of guessing lib, use ${CMAKE_INSTALL_LIBDIR}. One line, correct everywhere. If you omit destinations entirely, these are the defaults used.
install(EXPORT) versus export(), build tree versus install tree
There are two export commands and they are not interchangeable:
install(EXPORT ...)writes the file for the installed location. Paths are relative to the package itself, so the result is relocatable. You can move or copy the installed prefix and it still works.export(EXPORT ...)writes a file for the build tree, with paths pointing into yourbuild/. Use it only for internal purposes, like letting a test in the same build find your library. It is explicitly not relocatable.
Why
Relocatability is the whole deal with
install(EXPORT). The generated file refers to “the library next to me” instead of absolute paths like/home/you/build/.... Move the installed folder anywhere and the package still works. That is what letsfind_packagework from any install prefix.
Install the app too
install(TARGETS myapp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
When you do not need an export set, the TARGETS form without EXPORT is enough.
Pitfall
If headers are installed but consumers still cannot find them, check two things. Headers must be listed in
install(FILES ...), they do not install themselves. And the include path must beinclude/calc, matching the#include <calc/calc.h>you taught consumers in lesson 9.
Tip
Develop the habit. In CI, run
cmake --install build --prefix /tmp/stageand inspect/tmp/stage. A five-second check that your install rules are complete before you even think about packaging.
Read the docs