Reader small image

You're reading from  Modern CMake for C++

Product typeBook
Published inFeb 2022
PublisherPackt
ISBN-139781801070058
Edition1st Edition
Tools
Right arrow
Author (1)
Rafał Świdziński
Rafał Świdziński
author image
Rafał Świdziński

Rafał Świdziński works as a staff engineer at Google. With over 10 years of professional experience as a full stack developer, he has been able to experiment with a vast multitude of programming languages and technologies. During this time, he has been building software under his own company and for corporations including Cisco Meraki, Amazon, and Ericsson. Originally from Łódź, Poland, he now lives in London, UK, from where he runs a YouTube channel, "Smok," discussing topics related to software development. He tackles technical problems, including real-life and work-related challenges encountered by many people in the field. Throughout his work, he explains the technical concepts in detail and demystifies the art and science behind the role of software engineer. His primary focus is on high-quality code and the craftsmanship of programming.
Read more about Rafał Świdziński

Right arrow

Chapter 11: Installing and Packaging

Our project has been built, tested, and documented. Now, it's finally time to release it to our users. This chapter is mainly about the two last steps we'll need to take to do that: installation and packaging. These are advanced techniques that build on top of everything we've learned so far: managing targets and their dependencies, transient usage requirements, generator expressions, and much more.

Installation allows our project to be discoverable and accessible system-wide. In this chapter, we will cover how to export targets so that another project can use them without installation, as well as how to install our projects so that they can easily be used by any program on the system. In particular, we'll learn how to configure our project so that it can automatically put different artifact types in the correct directory. To handle more advanced scenarios, we'll introduce low-level commands for installing files and...

Technical requirements

You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Modern-CMake-for-Cpp/tree/main/examples/chapter11.

To build examples provided in this book always use recommended commands:

cmake -B <build tree> -S <source tree>
cmake --build <build tree>

Be sure to replace placeholders <build tree> and <source tree> with appropriate paths. As a reminder: build tree is the path to target/output directory, source tree is the path at which your source code is located.

Exporting without installation

How can we make the targets of project A available to the consuming project B? Usually, we'd reach for the find_package() command, but that would mean that we'd need to create a package and install it on the system. That approach is useful, but it takes some work. Sometimes, we just need a really quick way to build a project and make its targets available for other projects.

We could save some time by including the main listfile of A: it contains all the target definitions already. Unfortunately, it also potentially contains a lot of other things: global configuration, requirements, CMake commands with side effects, additional dependencies, and perhaps targets that we don't want in B (such as unit tests). So, let's not do that. It's better to achieve this by providing a target export file that the consuming project, B, can include with the include() command:

cmake_minimum_required(VERSION 3.20.0)
project(B)
include(/path...

Installing projects on the system

In Chapter 1, First Steps with CMake, we indicated that CMake offers a command-line mode that installs built projects on the system:

cmake --install <dir> [<options>]

<dir> is the path to the generated build tree (required). Our <options> are as follows:

  • --config <cfg>: This picks the build configuration for a multi-configuration generator.
  • --component <comp>: This limits the installation to the given component.
  • --default-directory-permissions <permissions>: This sets the default permissions for the installed directories (in <u=rwx,g=rx,o=rx> format).
  • --prefix <prefix>: This specifies the non-default installation path (stored in the CMAKE_INSTALL_PREFIX variable). It defaults to /usr/local for Unix-like systems and c:/Program Files/${PROJECT_NAME} for Windows.
  • -v, --verbose: This makes the output verbose (this can also be achieved by setting the VERBOSE environment...

Creating reusable packages

We have used find_package() extensively in previous chapters. We saw how convenient it is and how it simplifies the whole process. To make our project accessible through this command, we need to complete a few steps so that CMake can treat our project as a coherent package:

  • Make our targets relocatable.
  • Install the target export file to a standard location.
  • Create a config-files and version file for the package.

Let's start from the beginning: why do targets need to be relocatable and how can we do this?

Understanding the issues with relocatable targets

Installation solves many problems but unfortunately, it also introduces some complexity: not only is CMAKE_INSTALL_PREFIX platform-specific but it can also be set by the user at the installation stage with the --prefix option. However, target export files are generated before the installation, during the build stage, at which point we don't know where the installed...

Defining components

We'll start talking about package components by clearing up some possible confusion around the term component. Look at the full signature for find_package():

find_package(<PackageName> [version] [EXACT] [QUIET]
[MODULE]
  [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])

The components that are mentioned here shouldn't be conflated with the COMPONENT keyword that's used in the install() command. They are different concepts that must be understood separately, despite sharing the same name. We'll look at this in more detail in the following subsections.

How to use components in find_package()

When we call find_package() with a list of COMPONENTS or OPTIONAL_COMPONENTS, we tell CMake that we're only interested in packages that...

Packaging with CPack

Building projects from a source has its benefits, but it can take a long time and introduce a lot of complexity. This isn't the best experience for end users who just want to use the package, especially if they aren't developers themselves. A much more convenient form of software distribution is to use binary packages that contain compiled artifacts and other static files that are needed by the runtime. CMake supports generating multiple kinds of such packages through a command-line tool called cpack.

The following table lists the available package generators:

Most of these generators have extensive configurations. It is beyond the scope of this book to delve into all their details, so be sure to check out the full documentation, which can be found in the Further reading section. Instead, we'll focus on the general use case.

Note

Package generators shouldn't be confused with buildsystem generators (Unix Makefiles...

Summary

Writing installation scripts in a cross-platform way is an incredibly complex task without a tool such as CMake. While it still requires a little bit of work to set up, it's a much more streamlined process that ties closely to all the other concepts and techniques we've used so far in this book.

First, we learned how to export CMake targets from projects so that they can be consumed in other projects without installing them. Then, we learned how to install projects that had already been configured for this purpose.

After that, we started exploring the basics of installation by starting with the most important subject: installing CMake targets. We now know how CMake handles different destinations for various artifact types and how to deal with public headers that are somewhat special. To manage these installation steps at lower levels, we discussed other modes of the install() command, including installing files, programs, and directories and invoking scripts...

Further reading

To learn more about the topics that were covered in this chapter, take a look at the following resources:

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Modern CMake for C++
Published in: Feb 2022Publisher: PacktISBN-13: 9781801070058
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Rafał Świdziński

Rafał Świdziński works as a staff engineer at Google. With over 10 years of professional experience as a full stack developer, he has been able to experiment with a vast multitude of programming languages and technologies. During this time, he has been building software under his own company and for corporations including Cisco Meraki, Amazon, and Ericsson. Originally from Łódź, Poland, he now lives in London, UK, from where he runs a YouTube channel, "Smok," discussing topics related to software development. He tackles technical problems, including real-life and work-related challenges encountered by many people in the field. Throughout his work, he explains the technical concepts in detail and demystifies the art and science behind the role of software engineer. His primary focus is on high-quality code and the craftsmanship of programming.
Read more about Rafał Świdziński