Skip to content

How Does CMake Work

CMakeLists.txt File

You will write most commands and instructions in CMakeLists.txt files. The CMakeLists.txt is what determines what artifacts of your project shall be compiled and linked. You will likely see multiple of these files spread across the project at both the top-level directory and in multiple subfolders. In a sense, you might consider these somewhat equivalent to makefiles but hopefully less abstract.

######################################
# CMake Example Project              #
######################################
cmake_minimum_required(VERSION 3.18.0)      # Minimum CMake version supported
project(CMake-Example-Prj                   # Project arributes
                    VERSION 1.0 
                    LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 11)                    # C standard utilized

add_executable(cmake-example-project)       # Create exeucatble target

# Add sources to target executable
target_sources(cmake-example-project PRIVATE main.c)    

A couple importatnt things to note while we're here:

  1. For every project, there must be a CMakeLists.txt at the root of the project!

  2. Said root/top-level CMakeLists.txt file will usually define the following parameters at the top of the file:

    • Minimum cmake version that can be used
    • Project Attributes (i.e. name, version, languages used, etc.)
    • The C/C++ standard

We'll play with these more when we get into our main project but we highly recommend looking at the official CMake documentation and the JetBrains-CLion documentation for more on CMakeLists.txt files.

  1. Clion-CMakeLists.txt
  2. CMake-Writing CMakeLists Files
Note on C/C++ Programming Standards

See the below links for more information about C/C++ programming standards. These topics are outside the scope of this guide but might still worth a read if you're interested in understanding more nuanced details about the language.

CMake Language

CMake has it's own language (affenctionately refered to as the CMake Language). While we won't cover all the features of it, for the most part, it has similar constructs to regular programming languages and other build systems (i.e. variables, conditionals, libraries, targets, etc.) We'll touch on some more advance concepts/constructs as we go along such as toolchain files but there are some foundational concepts that would be useful to introduce now.

Note

The cmake-language manual and Writing CMakeLists Files documents are good sources to reference for examples of how to work with CMake.

Targets

Targets are the most important and most used construct in CMake. They are somewhat similar to how objects and the like work in modern programming languages. Each object instantiated has a class/type and associated methods and properties. Following this analogy, we can express two existing types/classes of targets: application targets and library targets. Through the use of targets and their respective properties, we can instruct CMake to build our project in a more systemic manner.

The following are the two main types or classes of targets which we shall detail below.

1. Application Targets

Application targets represent the final executable artifact of the project. This usually manifests itself in either .exe, .elf, .bin, or a .hex file that can be executed on some target platform (i.e. windows, linux, arm, etc.).

In order to define a application target, you would utilize the add_executable command. An important thing to note is that unlike library targets, there should only be one active application target per project and each application target needs at least one source file.

Example Usage:

Let's take the example of the simple helloworld.exe program. Let's assume as well that there is only the main.c file to accompany the project.

######################################
# CMake Example Project              #
######################################

# Project Metadata
cmake_minimum_required(VERSION 3.18.0)
project(hello_world_app VERSION 1.0 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 11)

# Application Config
add_executable(helloworld)
target_sources(helloworld PRIVATE main.c)

2. Library Targets

Library targets represent modules that can be consumed and linked to during the build & compilation process. Think of these more like a collection of source and header files that the application target/executable needs in order to be properly built or configured.

3. Custom Targets

CMake also gives the user the ability to create a custom target for instances where the user may want to invoke a custom command during the build process such as obtaining a firmware signature or even running an external Python script.

Variables

How to Build/Compile your Project

  1. Create a new folder directory for build outputs and artifacts.
    1. Most developers tend to call this folder 'build' but you can name it anything you want.
  2. Using the ternminal navigate to the build directory
  3. Type execute the cmake command with the -s argument.
    1. The -s argument specifies the source directory where the top level CMakeLists.txt file is located

A few things to note before moving on:

  1. Always run the cmake command from your build directory. If you were to run the cmake command from root directory without setting the correct parameters all the build artifacts would start to populate the root of your project.

NOTE: There are methods to set up CMake to where you can run the cmake command from the top level directory and still have artifacts generated in the build directory. For now though, it's much easier just to run the command from the build directory

What Happens when Invoking cmake command

When the cmake command is executed in the command environment, there is a three step process to convert your CMake files and source code to an application binary and or desired output(s).

CMake Stages/Process:

  1. Configuration - The process starts here when the user invokes the cmake command. More accurately, it involves CMake parsing the root of your project for the top-level directory, handling any input arguments/parameters, reading from and or updating stored cache variables, and preparing the build directory for the build artifacts.

  2. Generation - An intermediary step where CMake takes all the parameters and information from the Configuration step to output files corresponding to the project's chosen build system/tool. For example, if the project specified that Ninja was the chosen generator/build tool, after invoking the cmake command, the user should expect to see a build.ninja file and other relevant artifacts in their specified build directory.

  3. Building - Simply the stage of the process where the user builds the application using their specified build tool to obtain the desired artifacts (application binaries, test results, etc). Note that at this stage, the chosen build tool shall work in conjunction with the specified compiler toolchain.