From sources to executables

Questions

  • How do we use CMake to compile source files to executables?

Objectives

  • Learn what tools available in the CMake suite.

  • Learn how to write a simple CMakeLists.txt.

  • Learn the difference between build systems, build tools, and build system generator.

  • Learn to distinguish between configuration, generation, and build time.

What is CMake and why should you care?

Software is everywhere and so are build systems. Whenever you run a piece of software, anything from calendar apps to computationally-intensive programs, there was a build system involved in transforming the plain-text source code into binary files that could run on the device you are using.

CMake is a build-system generator: it provides a family of tools and a domain-specific language (DSL) to describe what the build system should achieve when the appropriate build tools are invoked. The DSL is platform- and compiler-agnostic: you can reuse the same CMake scripts to obtain native build systems on any platform.

../_images/build-systems.svg

On GNU/Linux, the native build system will be a collection of Makefile-s. The make build tool uses these Makefile-s to transform sources to executables and libraries. CMake abstracts the process of generating the Makefile-s away into a generic DSL.

A CMake-based build system:

  • can bring your software closer to being platform- and compiler-agnostic.

  • has good support within many integrated development environments (IDEs).

  • automatically tracks and propagates internal dependencies in your project.

  • is built on top of well-maintained functionality for automated dependency detection.

Hello, CMake!

There are few things to note here:

  1. Any CMake build system will invoke the following commands in its root CMakeLists.txt:

  2. The case of CMake commands does not matter: the DSL is case-insensitive. However, the plain-text files that CMake parses must be called CMakeLists.txt and the case matters! The variable names are also case sensitive!

  3. The command to add executables to the build system is, unsurprisingly, add_executable:

  4. Using CMake you can abstract the generation of the build system and also the invocation of the build tools.

Put your CMakeLists.txt under version control

All CMake-related files will evolve together with your codebase. It’s a good idea to put them under version control. On the contrary, any of the generated native build-system files, e.g. Makefile-s, should not be version-controlled.

A complete toolchain

The family of tools provided with CMake offers a complete toolchain to manage the development cycle: from sources to build artifacts, testing, and deployment. We refer to these stages as CMake times and each tool is appropriate at a specific time. In this workshop, we will discuss:

  • CMake time or configure time. This is the stage when cmake is invoked to parse the CMakeLists.txt in your project, configure and generate the build system.

  • Build time. This is handled by the native build tools, but, as we have seen, these can be effectively wrapped by cmake itself.

  • CTest time or test time. At this stage, you will test your build artifacts.

../_images/cmake-times.jpg

You can manage all the stages of a software project’s lifetime with the tools provided by CMake. This figure shows all these stages (times) and which tool is appropriate for each. The figure is reproduced from CMake Cookbook and is licensed under the terms of the CC-BY-SA.

Producing libraries

CMake can of course be used to produce libraries as well as executables. The relevant command is add_library:

You can link libraries into executables with target_link_libraries:

Executables and libraries are targets

We will encounter the term target repeatedly. In CMake, a target is any object given as first argument to add_executable or add_library. Targets are the basic atom in CMake. Whenever you will need to organize complex projects, think in terms of its targets and their mutual dependencies. The whole family of CMake commands target_* can be used to express chains of dependencies and is much more effective than keeping track of state with variables. We will clarify these concepts in Target-based build systems with CMake.

Exercise 1: Producing libraries

You can find a scaffold project in the content/code/day-1/01_libraries-cxx folder.

  1. Write a CMakeLists.txt to compile the source files Message.hpp and Message.cpp into a library. Do not specify the type of library, shared or static, explicitly.

  2. Add an executable from the hello-world.cpp source file.

  3. Link the library into the executable.

A working solution is in the solution subfolder.

What kind of library did you get? Static or shared?

Keypoints

  • CMake is a build system generator, not a build system.

  • You write CMakeLists.txt to describe how the build tools will create artifacts from sources.

  • You can use the CMake suite of tools to manage the whole lifetime: from source files to tests to deployment.

  • The structure of the project is mirrored in the build folder.