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 .cmake file 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 your build/. 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 lets find_package work 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 be include/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/stage and inspect /tmp/stage. A five-second check that your install rules are complete before you even think about packaging.

Read the docs