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

Understanding the CMake build process

CMake's build process works in two steps, as shown in the following diagram. First, if it's invoked without any special flags, CMake scans the system for any usable toolchains during the configuration process and then decides what its output should be. The second step, which is when cmake --build is invoked, is the actual compilation and building process:

Figure 1.3 – CMake's two-stage build process

Figure 1.3 – CMake's two-stage build process

The standard output is Unix Makefiles unless the only detected compiler is Microsoft Visual Studio, in which case a Visual Studio solution (.sln) will be created.

To change the generator, you can pass the -G option to CMake, like this:

cmake .. -G Ninja

This will generate files to be used with Ninja (, an alternative build generator. Many generators are available for CMake. A list of the ones that are supported natively can be found on CMake's website:

There are two main types of generators – the ones where there are many Makefile flavors and Ninja generators, which are generally used from the command line, and the ones that create build files for an IDE such as Visual Studio or Xcode.

CMake addition differentiates between single-configuration generators and multi-configuration generators. For single-configuration generators, the build files have to be rewritten each time the configuration is changed; multi-configuration build systems can manage different configurations without the need to regenerate. Although the examples in this book use single-configuration generators, they would also work on multi-configuration generators. For most of the examples, the chosen generator is irrelevant; otherwise, it will be mentioned:

In addition, there are extra generators that use a normal generator but also produce project information for an editor or IDE, such as Sublime Text 2, Kate Editor, Code::Blocks, and Eclipse. For each, you can select whether the editor should use Make or Ninja to internally build the application.

After the call, CMake will create a lot of files in the build folder, with the most notable being the CMakeCache.txt file. This is where all the detected configurations are stored. Note that when you're using cmake-gui, the first step is split into configuring the project and generating the build file. However, when it's run from the command line, the steps are merged into one. Once configured, all the build commands are executed from the build folder.

Source folders and build folders

In CMake, two logical folders exist. One is the source folder, which contains a hierarchical set of projects, while the other is a build folder, which contains the build instructions, cache, and all the generated binaries and artifacts.

The root of the source folder is wherever the top CMakeLists.txt file is located. The build folder can be placed inside the source folder, but some people prefer to have it in another location. Both are fine; note that for the examples in this book, we decided to keep the build folder inside the source folder. The build folder is often called just build, but it can take any name, including prefixes and suffixes for different platforms. When using a build folder inside the source tree, it is a good idea to add it to .gitignore so that it does not get checked in accidentally.

When configuring a CMake project, the project and folder structure of the source folder is recreated inside the build folder so that all the build artifacts are in the same position. In each mapped folder, there is a subfolder called CMakeFiles that contains all the information that's generated by CMake's configuration step.

The following code shows an example structure for a CMake project:

├── chapter_1
│   ├── CMakeLists.txt
│   └── src
│       └── main.cpp
├── CMakeLists.txt

When you execute the CMake configuration, the file structure of the CMake project is mapped into the build folder. Each folder containing a CMakeLists.txt file will be mapped and a subfolder called CMakeFiles will be created, which contains the information that's used by CMake for building:

├── build
│   ├── chapter_1
│   │   └── CMakeFiles
│   └── CMakeFiles

So far, we have used existing projects to learn about the CMake build process. We learned about the configuration and the build step, as well as generators, and that we need CMakeLists.txt files to pass the necessary information to CMake. So, let's go a step further and see what the CMakeLists.txt files look like and how the CMake language works.