Book Image

CMake Best Practices

By : Dominik Berner, Mustafa Kemal Gilor
5 (2)
Book Image

CMake Best Practices

5 (2)
By: Dominik Berner, Mustafa Kemal Gilor

Overview of this book

CMake is a powerful tool used to perform a wide variety of tasks, so finding a good starting point for learning CMake is difficult. This book cuts to the core and covers the most common tasks that can be accomplished with CMake without taking an academic approach. While the CMake documentation is comprehensive, it is often hard to find good examples of how things fit together, especially since there are lots of dirty hacks and obsolete solutions available on the internet. This book focuses on helping you to tie things together and create clean and maintainable projects with CMake. You'll not only get to grips with the basics but also work through real-world examples of structuring large and complex maintainable projects and creating builds that run in any programming environment. You'll understand the steps to integrate and automate various tools for improving the overall software quality, such as testing frameworks, fuzzers, and automatic generation of documentation. And since writing code is only half of the work, the book also guides you in creating installers and packaging and distributing your software. All this is tailored to modern development workflows that make heavy use of CI/CD infrastructure. By the end of this CMake book, you'll be able to set up and maintain complex software projects using CMake in the best way possible.
Table of Contents (22 chapters)
Part 1: The Basics
Part 2: Practical CMake – Getting Your Hands Dirty with CMake
Part 3: Mastering the Details

Building your first project

Now, it's time to get your hands dirty and see whether your installation worked. We have provided an example of a simple hello world project that you can download and build right away. Open a console, type in the following, and you'll be ready to go:

git clone
cd CMake-Best-Practices/chapter_1
mkdir build
cd build 
cmake ..
cmake -–build .

This will result in an executable called Chapter_1 that prints out Welcome to CMake Best Practices on the console.

Let's have a detailed look at what happened here:

  1. First, the example repository is checked out using Git and then the build folder is created. The file structure of the example CMake project will look like this before the build:
    ├── CMakeLists.txt
    └── build
    └── src
        └── main.cpp

Apart from the folder containing the source code, there is a file called CMakeLists.txt. This file contains the instructions for CMake on how to create build instructions for the project and how to build it. Every CMake project has a CMakeLists.txt file at the root of the project, but there might be many files with that name in various subfolders.

  1. After cloning the repository, the build process is started with cmake. CMake's build process is a two-stage process. The first step, which is usually called configuration, reads the CMakeLists.txt file and generates an instruction for the native build toolchain of the system. In the second step, these build instructions are executed and the executables or libraries are built.

During the configuration step, the build requirements are checked, the dependencies are resolved, and the build instructions are generated.

  1. Configuring a project also creates a file called CMakeCache.txt that contains all the information that's needed to create the build instructions. The next call to cmake --build . executes the build by internally calling CMake; if you are on Windows, it does so by invoking the Visual Studio compiler. This is the actual step for compiling the binaries. If everything went well, there should be an executable named Chapter1 in the build folder.

For brevity, we cd'd into the build directory in the previous examples and used relative paths to find the source folders. This is often convenient, but if you want to call CMake from somewhere else, you can use the --S option to select the source file and the --B option to select the build folder:

cmake -S /path/to/source -B /path/to/build
cmake -build /path/to/build

Explicitly passing the build and source directories often comes in handy when using CMake in a continuous integration environment since being explicit helps with maintainability. It is also helpful if you want to create different build directories for different configurations, such as when you're building cross-platform software.

A minimal CMakeLists.txt file

For a very simple hello world example, the CMakeLists.txt file only consists of a few lines of instructions:

cmake_minimum_required(VERSION 3.21)
  DESCRIPTION "A simple project to demonstrate basic CMake 
    usage" LANGUAGES CXX)
target_sources(Chapter1 PRIVATE src/main.cpp)

Let's understand these instructions in a bit more detail:

  • The first line defines the minimum version of CMake that's required to build this project. Every CMakeLists.txt file starts with this directive. This is used to warn the user if the project uses features of CMake that are only available from a certain version upward. Generally, we recommend setting the version to the lowest version that supports the features that are used in the project.
  • The next directive is the name, version, and description of the project to be built, followed by the programming languages that are used in the project. Here, we use CXX to mark this as a C++ project.
  • The add_executable directive tells CMake that we want to build an executable (as opposed to a library or a custom artifact, which we will cover later in this book).
  • The target_sources statement tells CMake where to look for the sources for the executable called Chapter1 and that the visibility of the sources is limited to the executable. We will go into the specifics of the single commands later in this book.

Congratulations – you are now able to create software programs with CMake. But to understand what is going on behind the commands, let's look at the CMake build process in detail.