Building with CMake
CMake is the de facto standard way to build C++ projects. You describe your targets — executables and libraries, their source files, options and dependencies — in a CMakeLists.txt, and CMake generates the actual build files for your platform and tools: Makefiles, Ninja, Visual Studio or Xcode projects.
A minimal project
For the inventory program from the previous lesson:
cmake_minimum_required(VERSION 3.20) project(inventory LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_executable(inventory main.cpp product.cpp) target_compile_options(inventory PRIVATE -Wall -Wextra -pedantic)
cmake -S . -B build # configure: generate build files into ./build cmake --build build # compile and link ./build/inventory
The build folder holds everything generated, keeping your source tree clean — add it to .gitignore. After editing code, only cmake --build build is needed; CMake re-runs the configure step itself when CMakeLists.txt changes.
Targets and properties
Modern CMake is organised around targets. Settings are attached to a target with target_* commands, and PUBLIC/PRIVATE decides whether they also apply to targets that use it:
cmake_minimum_required(VERSION 3.20) project(shop LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # A library with the reusable code add_library(inventory src/product.cpp src/order.cpp) target_include_directories(inventory PUBLIC include) # users get the include path too target_compile_options(inventory PRIVATE -Wall -Wextra) # only for building the library # The program, linking the library add_executable(shop src/main.cpp) target_link_libraries(shop PRIVATE inventory) # Tests enable_testing() add_executable(inventory_tests tests/product_tests.cpp) target_link_libraries(inventory_tests PRIVATE inventory) add_test(NAME inventory_tests COMMAND inventory_tests)
With tests registered, ctest --test-dir build runs them all.
Build types
cmake -S . -B build-debug -DCMAKE_BUILD_TYPE=Debug # -g, no optimisation cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release # -O3, no debug info cmake -S . -B build -G Ninja # use the fast Ninja generator
Multi-configuration generators (Visual Studio, Xcode) choose the type at build time instead: cmake --build build --config Release.
Using libraries
Installed libraries are found with find_package; others can be downloaded at configure time with FetchContent:
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 11.1.4
)
FetchContent_MakeAvailable(fmt)
target_link_libraries(shop PRIVATE fmt::fmt)
Package managers such as vcpkg and Conan integrate with CMake to install and version third-party libraries for every platform.
Sanitizers in a build
option(ENABLE_SANITIZERS "Build with AddressSanitizer and UBSan" OFF)
if(ENABLE_SANITIZERS)
target_compile_options(shop PRIVATE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_options(shop PRIVATE -fsanitize=address,undefined)
endif()
cmake -S . -B build-asan -DENABLE_SANITIZERS=ON
IDE support
CLion, Visual Studio, VS Code (with the CMake Tools extension) and Qt Creator open a folder containing CMakeLists.txt directly and build, run and debug from it. CMake can also export compile_commands.json (-DCMAKE_EXPORT_COMPILE_COMMANDS=ON), which tools like clangd and clang-tidy use to understand your project.
Other build systems
- Meson — clean syntax, fast, popular in Linux projects.
- Bazel — for very large, multi-language codebases.
- Plain Makefiles — fine for small Unix-only projects (see the C course's make lesson).
Try it yourself
Create a folder with this program and a CMakeLists.txt containing add_executable(hello main.cpp), then configure and build it with CMake.
Built with CMake!