CMake and Build Systems¶
CMake is the de facto standard C++ build system generator. Defines targets, dependencies, and compiler options in a declarative CMakeLists.txt.
Key Facts¶
- CMake generates native build files (Makefiles, Ninja, VS solutions, Xcode projects)
- Modern CMake (3.x) is target-based:
target_*commands replace globalinclude_directories/link_libraries - Targets:
add_executable,add_library(STATIC, SHARED, INTERFACE, OBJECT) target_link_librariespropagates include dirs, compile defs, and flags throughPUBLIC/PRIVATE/INTERFACEfind_packagelocates installed libraries;FetchContentdownloads and builds dependencies- Generator expressions:
$<BUILD_INTERFACE:...>/$<INSTALL_INTERFACE:...>for context-dependent settings CMAKE_CXX_STANDARDsets C++ standard version (17, 20, 23)- Out-of-source builds:
cmake -B build -S .thencmake --build build - Presets (CMakePresets.json): standardize build configurations across team
ccache/sccachefor compilation caching;ninjafor faster builds
Patterns¶
Minimal Project¶
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(myapp src/main.cpp src/app.cpp)
target_include_directories(myapp PRIVATE ${CMAKE_SOURCE_DIR}/include)
Library + Executable¶
# Library
add_library(mylib
src/core.cpp
src/utils.cpp
)
target_include_directories(mylib PUBLIC include)
target_compile_features(mylib PUBLIC cxx_std_20)
# Executable using the library
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
Finding Dependencies¶
# System-installed library
find_package(Threads REQUIRED)
find_package(Boost 1.80 REQUIRED COMPONENTS filesystem system)
target_link_libraries(myapp PRIVATE
Threads::Threads
Boost::filesystem
Boost::system
)
# Download and build dependency (C++17)
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.2.1
)
FetchContent_MakeAvailable(fmt)
target_link_libraries(myapp PRIVATE fmt::fmt)
Testing with CTest¶
enable_testing()
# With GoogleTest
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)
add_executable(tests test/test_core.cpp)
target_link_libraries(tests PRIVATE mylib GTest::gtest_main)
include(GoogleTest)
gtest_discover_tests(tests)
Compiler Warnings¶
# Per-target warnings (modern approach)
target_compile_options(mylib PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
)
Build Commands¶
# Configure
cmake -B build -S . -G Ninja -DCMAKE_BUILD_TYPE=Release
# Build
cmake --build build --parallel $(nproc)
# Run tests
cd build && ctest --output-on-failure
# Install
cmake --install build --prefix /usr/local
CMakePresets.json¶
{
"version": 6,
"configurePresets": [
{
"name": "dev",
"binaryDir": "build/dev",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "release",
"binaryDir": "build/release",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
]
}
Gotchas¶
- Issue: Using
include_directories(global) instead oftarget_include_directories-> pollutes all targets -> Fix: Always usetarget_*commands with PRIVATE/PUBLIC/INTERFACE - Issue:
find_packagefails because library not in CMAKE_PREFIX_PATH -> Fix: Set-DCMAKE_PREFIX_PATH=/path/to/libat configure time or useFetchContentfor self-contained builds - Issue: Forgetting
CMAKE_CXX_STANDARD_REQUIRED ON-> CMake silently falls back to older standard -> Fix: Always set bothCMAKE_CXX_STANDARDandCMAKE_CXX_STANDARD_REQUIRED - Issue: In-source builds create mess of generated files alongside source -> Fix: Always use out-of-source build:
cmake -B build