Link Search Menu Expand Document

Basics

To effectively use cmkr it helps to understand the basic concepts of CMake. This page serves as an introduction to CMake for beginners. Links to the CMake documentation are included for reference.

Projects

A CMake project is a collection of targets. In the context of libraries the project usually corresponds to the package that other projects can depend on.

Visual Studio: a CMake project corresponds to a solution.

Targets

The basic unit of CMake is called a target. A target (also referred to as binary target in the CMake documentation) corresponds to an executable or static/dynamic library you can build.

Visual Studio: a target corresponds to a project.

Most important target types:

Type Purpose
Executable Program you run on your computer (.exe)
Static library Library code compiled into the final executable (.lib, .a)
Dynamic library Library code loaded at runtime (.dll, .so, .dylib)
Interface Used for organizational purposes (common flags/includes/libraries)

There are also other pseudo targets, but they are not important for now.

Target Properties

Targets have a collection of properties that describe how the compiler builds them.

The most commonly-used properties:

See the CMake documentation for an exhaustive list of target properties.

Important: The term link has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line, CMake also (transitively) propagates properties of the target you link to based on their visibility.

You should think of linking in CMake as depending on.

The propagation of properties depends on their visibility:

  • Private: properties that are only used when building the target itself.
  • Interface: properties that are used when depending on this target.
  • Public: combination of private and interface.

In practice you default to private, unless consumers of your library require the property to build their target. In that case you use public.

Example

The most intuitive example is with include directories. Imagine there are two targets:

  1. StringUtils: A library with string utilities.
    • Sources: StringUtils/src/stringutils.cpp
    • Include directories: StringUtils/include
  2. DataProcessor: An executable that uses functionality from StringUtils to process some data.
    • Sources: DataProcessor/src/main.cpp
    • Link libraries: StringUtils

The include directories property of StringUtils has to be public. If it was private, the DataProcessor target would fail to #include <stringutils.hpp> since the include directories property is not propagated.

The cmake.toml for this example would look something like this:

[project]
name = "DataProcessor"

[target.StringUtils]
type = "static"
sources = ["StringUtils/src/stringutils.cpp"]
headers = ["StringUtils/include/stringutils.hpp"]
include-directories = ["StringUtils/include"] # public (default for libraries)

[target.DataProcessor]
type = "executable"
sources = ["DataProcessor/src/main.cpp"]
link-libraries = ["StringUtils"] # private (default for executables)

The generated CMakeLists.txt (simplified) looks like this:

project(DataProcessor)

# Target: StringUtils
add_library(StringUtils STATIC
    "StringUtils/include/stringutils.hpp"
    "StringUtils/src/stringutils.cpp"
)
target_include_directories(StringUtils PUBLIC
	"StringUtils/include"
)

# Target: DataProcessor
add_executable(DataProcessor
    "DataProcessor/src/main.cpp"
)
target_link_libraries(DataProcessor PRIVATE
    StringUtils
)

CMake Phases

CMake works in three phases:

  1. Configure: Execute CMakeLists.txt (find dependencies, create targets, OS/compiler-specific conditions, etc).
  2. Generate: Generate build files (propagates target properties and handles generator expressions).
  3. Build: Execute the compiler to produce executables/libraries.

CMake keeps generated files separate from your source in a build directory. You specify a generator (Visual Studio/Ninja) and build type (Debug/Release) during configuration.

# Configure project and generate build files in build directory
cmake -B build -DCMAKE_BUILD_TYPE=Release

# Build the project
cmake --build build --config Release

We specify both CMAKE_BUILD_TYPE and --config to be compatible with generators that support multiple configurations at once ((like Visual Studio/Xcode/Ninja Multi-Config).

Build Types:

  • Debug: No optimization, debug symbols
  • Release: Full optimization, no debug symbols
  • RelWithDebInfo: Medium optimizations, debug symbols
  • MinSizeRel: Optimize for size, no debug symbols

Generators: CMake auto-detects (Makefiles on Linux/macOS, Visual Studio on Windows), but you can specify explicitly with -G during the configure step.