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
- CMake configures (generates a build system in a build directory) and then builds with Ninja, Make, MSBuild, or Xcode.
- Think in targets:
add_library,add_executable, thentarget_*commands. PUBLIC,PRIVATE, andINTERFACEcontrol which requirements propagate to consumers.- Get dependencies with
find_package(installed packages),FetchContent(source), or vcpkg/Conan. - Use
CMakePresets.jsonfor shared configure, build, and test settings, and toolchain files for cross-compiling. - Test with CTest, and export targets so other projects can
find_packageyours.
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
- Build Tools — Build systems compared
- Bazel — Hermetic builds for large monorepos
- C++ — The main language CMake builds
- C — C projects and libraries
- GCC — Compilers CMake drives
- Embedded C — Cross-compiling with toolchain files
- GNU Make — The build tool many CMake projects generate files for
- Ninja — The generator backend most CMake builds use
- Meson — An alternative declarative build system