CMake

CMake is the de facto standard build system generator for C and C++. Rather than building code itself, it reads CMakeLists.txt files and generates native build files — Ninja, Makefiles, Visual Studio solutions, Xcode projects — for whatever platform and compiler you use. Almost every major C++ library ships CMake support, IDEs understand it natively, and package managers integrate with it.

CMake has a reputation for confusing scripts, mostly earned by older styles that manipulated global variables. Modern CMake is built around targets and their usage requirements: each library declares what its consumers need (include directories, compile features, link dependencies), and CMake propagates it. Written this way, CMake projects are short, portable, and easy to consume.

TL;DR

Quick Example

A library, an application, and tests in modern CMake:

Shared presets so every developer and CI job configures the same way:

Core Concepts

Configure, Generate, Build

CMake runs in two phases. Configure evaluates CMakeLists.txt, finds compilers and dependencies, and stores results in CMakeCache.txt inside a build directory. Generate writes native build files. Then the native tool — usually invoked through cmake --build — compiles. Always build out of source so build artifacts never mix with source files.

Targets and Usage Requirements

If pricing exposes headers from include/ and uses C++20 types in them, those requirements are PUBLIC. If it uses a logging library only in its .cpp files, that link is PRIVATE. Header-only libraries use add_library(name INTERFACE) with INTERFACE requirements. Consumers then need a single target_link_libraries line.

Dependencies

Link to the imported targets dependencies provide (GTest::gtest_main, OpenSSL::SSL, fmt::fmt) rather than variables of paths and flags.

Toolchains and Cross-Compilation

A toolchain file sets the target system, compilers, sysroot, and search behavior. Pass it with -DCMAKE_TOOLCHAIN_FILE=... or in a preset. The same project then builds for Linux, Windows, Arm boards, or microcontrollers. See Embedded C.

Testing and Packaging

CTest runs registered tests (add_test, or gtest_discover_tests for GoogleTest), in parallel, with labels and timeouts. install() and install(EXPORT ...) with a generated package config let other projects find_package your library; CPack builds archives and installers.

Generators and Build Types

Single-configuration generators use CMAKE_BUILD_TYPE (Debug, Release, RelWithDebInfo, MinSizeRel); multi-configuration generators choose at build time with --config.

Best Practices

Never Use Directory-Wide Commands for Target Settings

Prefer target_include_directories, target_compile_definitions, and target_compile_options over include_directories, add_definitions, and editing CMAKE_CXX_FLAGS, so settings stay with the targets that need them.

Require Language Features, Not Flags

Use target_compile_features(lib PUBLIC cxx_std_20) instead of hard-coding -std=c++20, so CMake picks the right flag for each compiler.

Commit Presets, Not Personal Settings

Share CMakePresets.json in the repository and keep machine-specific overrides in an uncommitted CMakeUserPresets.json.

Export a Compilation Database

Set CMAKE_EXPORT_COMPILE_COMMANDS=ON so clangd, clang-tidy, and other tools see the real compile flags. See Clang.

Common Mistakes

Global Flags and Include Paths

Everything Public

Ancient Minimum Versions

FAQ

What is CMake?

A cross-platform build system generator. It reads CMakeLists.txt files and generates native build files for tools such as Ninja, Make, Visual Studio, and Xcode, which then compile the project.

What is modern CMake?

A style built around targets and their usage requirements — target_link_libraries, target_include_directories, target_compile_features with PUBLIC, PRIVATE, and INTERFACE — instead of global variables and directory-wide settings.

What is the difference between PUBLIC, PRIVATE, and INTERFACE?

PRIVATE requirements apply only to the target itself, INTERFACE requirements apply only to its consumers, and PUBLIC requirements apply to both.

How do I add a third-party library with CMake?

Use find_package if it is installed with a CMake config, FetchContent to build it from source, or a package manager such as vcpkg or Conan, then link to the imported target it provides.

What are CMake presets?

A CMakePresets.json file defining named configure, build, test, and package settings — generator, build directory, cache variables, toolchain — so everyone and every CI job builds the project the same way.

Related Topics

References