CMake for C++ Projects: Targets, Presets, find_package and C++20 Modules
Key takeaways
Full CMake tutorial for C++: cross-platform builds, CMake 3.28+ features, CMakePresets, C++20 modules, find_package, targets, and CI/CD.
Troubleshooting: common CMake errors · Could NOT find ….
Introduction: “Build settings differ per platform—it’s hard to manage”
Common build pain points
When you develop C++ across platforms you often hit:
- Per-platform build files — separate
.vcxproj,Makefile, Xcode projects - Different tooling per teammate — Visual Studio, CLion, VS Code
- Dependency hell — Boost, OpenSSL, etc. installed in different paths
- Duplicated CI/CD — bespoke scripts in GitHub Actions, GitLab CI
- Slow builds — full rebuilds grow with project size CMake addresses this:
# ❌ Existing method (management by platform)
# Windows: msbuild MyProject.vcxproj
# Linux: make -f Makefile.linux
# macOS: xcodebuild -project MyProject.xcodeproj
# ✅ CMake (write once, build anywhere)
mkdir build && cd build
cmake ..
cmake --build .
flowchart LR
subgraph input[Inputs]
cmake[CMakeLists.txt]
preset["CMakePresets.json (3.19+)"]
end
subgraph cmake_tool[CMake]
gen[Generator]
end
subgraph output[Outputs]
make["Makefile (Linux)"]
vs["Visual Studio (Windows)"]
xcode["Xcode (macOS)"]
ninja["Ninja (all platforms)"]
end
cmake --> gen
preset --> gen
gen --> make
gen --> vs
gen --> xcode
gen --> ninja
Core CMake ideas
- Meta build system: CMake does not compile by itself; it generates platform build systems (Makefiles,
.sln, etc.). - Targets: Use
add_executable/add_libraryand wire deps withtarget_link_libraries,target_include_directories. - Presets (3.19+): Share settings via
CMakePresets.json. - C++20 modules (3.28+): First-class module support. Goals:
- CMake basics (minimal setup, targets, dependencies)
- CMake 3.28+ (presets, C++20 modules, FetchContent improvements)
- Real-world patterns (multi-platform, CI/CD, dependencies)
- Performance (parallel builds, ccache, PCH)
- Common mistakes and fixes
- Production patterns Requirements: CMake 3.20+ (3.28+ recommended for latest features)
Problem scenarios: build management pain
Scenario 1: “Each team member has different build settings.”
Context: Windows developers use Visual Studio, Linux developers use Makefile, and macOS developers use Xcode.
Problem: Settings are not synchronized because each person manages different build files.
Outcome: “Does it build in my environment?” Problem occurred
→ Share unified build settings with CMake + CMakePresets.json
Note: “Works on my machine” usually means generators, flags, or paths were not committed—reproducible one-line presets matter more.
Scenario 2: “The external library path varies depending on the environment.”
Context: Boost is C:\boost on Windows, /usr/local/boost on Linux, and /opt/homebrew/boost on macOS.
Problem: Build fails in other environments due to hardcoded paths
Outcome: Each team member must edit CMakeLists.txt
→ Automatic detection with find_package + CMAKE_PREFIX_PATH
Scenario 3: “Build is too slow”
Context: As the project grows, a full build takes 30 minutes.
Problem: Recompile all files and repeat header parsing every time.
Outcome: Decreased development productivity
→ Optimized with ccache, PCH, Ninja, and parallel builds
Scenario 4: “Build setup in CI/CD is complicated”
Context: Different build scripts in GitHub Actions, GitLab CI, and Jenkins
Problem: All CI files need to be modified when changing build settings
Outcome: Increased maintenance burden
→ Unify CI settings with CMakePresets.json
flowchart TB
subgraph Problems[Build management problems]
P1[Per-platform build files]
P2[Mismatched dependency paths]
P3[Slow builds]
P4[Duplicated CI/CD config]
end
subgraph Solutions[CMake solutions]
S1[Unified CMakeLists.txt]
S2[find_package discovery]
S3[ccache + Ninja + PCH]
S4[CMakePresets.json]
end
P1 --> S1
P2 --> S2
P3 --> S3
P4 --> S4
Minimal CMakeLists.txt
Hello World build
# CMakeLists.txt - CMake project file
# Minimum CMake version (3.20+)
cmake_minimum_required(VERSION 3.20)
# Project metadata (name, version, languages)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# C++ standard
set(CMAKE_CXX_STANDARD 20) # Use C++20
set(CMAKE_CXX_STANDARD_REQUIRED ON) # Error if C++20 unavailable
set(CMAKE_CXX_EXTENSIONS OFF) # Disable GNU extensions (portability)
# Executable target
# myapp: target name (output name)
# main.cpp: source file
add_executable(myapp main.cpp)
// main.cpp
#include <iostream>
int main() {
std::cout << "Hello, CMake!" << std::endl;
return 0;
}
# Create build directory (out-of-source)
mkdir build
cd build
# Configure (generate build system)
cmake ..
# Internal steps:
# 1. Parse CMakeLists.txt
# 2. Select Generator (platform defaults):
# - Linux: Unix Makefiles
# - Windows: Visual Studio 2022
# - macOS: Unix Makefiles
# 3. Generate build files:
# - Makefile (Linux)
# - .sln + .vcxproj (Windows)
# - .xcodeproj (macOS with Xcode)
# 4. Create CMakeCache.txt (settings cache)
# Build (compile with generated system)
cmake --build .
# Internal: invokes the native build tool
# - Linux: calls make
# - Windows: calls msbuild
# - Ninja: calls ninja
# Run
./myapp # Linux/macOS
# myapp.exe # Windows
CMake Build Process Detailed:
Step-by-step flow:
1. Configuration Phase (cmake ..)
┌─────────────────────────────────────┐
│ Parse CMakeLists.txt │
│ - project() → project metadata │
│ - add_executable() → register target │
│ - target_link_libraries() → deps │
├─────────────────────────────────────┤
│ Select Generator │
│ - Explicit: cmake -G "Ninja" .. │
│ - Auto: platform default │
├─────────────────────────────────────┤
│ Compiler Detection │
│ - Detect CMAKE_CXX_COMPILER │
│ - Test compiler features │
│ - Check ABI (32/64bit) │
├─────────────────────────────────────┤
│ Create Cache: CMakeCache.txt │
│ - Store all variables │
│ - Reuse on next run │
└─────────────────────────────────────┘
2. Generation Phase
┌─────────────────────────────────────┐
│ Generate build files │
│ Unix Makefiles: │
│ - Makefile │
│ - cmake_install.cmake │
│ Visual Studio: │
│ - MyProject.sln │
│ - myapp.vcxproj │
│ Ninja: │
│ - build.ninja │
├─────────────────────────────────────┤
│ compile_commands.json (optional) │
│ - CMAKE_EXPORT_COMPILE_COMMANDS=ON │
│ - LSP support (clangd, etc.) │
└─────────────────────────────────────┘
3. Build Phase (cmake --build .)
┌─────────────────────────────────────┐
│ Execute generated build system │
│ - make (Unix Makefiles) │
│ - msbuild (Visual Studio) │
│ - ninja (Ninja) │
├─────────────────────────────────────┤
│ Dependency graph execution │
│ - Header change → recompile sources │
│ - Library change → relink executables│
│ - Parallel builds (make -j, ninja) │
└─────────────────────────────────────┘
CMakeCache.txt role:
- Save all variables on first run
- Read cache on next run for speed
- Manual edits allowed (ccmake, cmake-gui)
- Delete for clean reconfigure (rm -rf build/)
Generator types:
- Unix Makefiles: traditional, slower
- Ninja: fast, excellent parallelism (recommended!)
- Visual Studio: Windows IDE integration
- Xcode: macOS IDE integration
- Ninja Multi-Config: multiple build types in one directory
Command cheat sheet
| Command | Meaning |
|---|---|
cmake_minimum_required(VERSION 3.20) | Minimum CMake version |
project(MyProject VERSION 1.0.0) | Project name and version |
set(CMAKE_CXX_STANDARD 20) | Use C++20 |
set(CMAKE_CXX_EXTENSIONS OFF) | Disable GNU extensions |
add_executable(myapp main.cpp) | Create executable target |
cmake .. | Configure from parent CMakeLists.txt |
cmake --build . | Portable build command |
Notable CMake versions
| Version | Highlights |
|---|---|
| 3.19 | CMakePresets.json |
| 3.20 | C++23 support |
| 3.23 | FILE_SET (headers) |
| 3.25 | LINUX variable |
| 3.26 | SYSTEM property tweaks |
| 3.28 | Native C++20 modules |
Targets: executables and libraries
Executables
# Single source
add_executable(myapp main.cpp)
# Multiple sources
add_executable(myapp
src/main.cpp
src/utils.cpp
src/config.cpp
)
# Using variables
set(SOURCES
src/main.cpp
src/utils.cpp
)
add_executable(myapp ${SOURCES})
Libraries
# Static library (.a / .lib)
add_library(mylib STATIC
src/lib.cpp
src/helper.cpp
)
# Shared library (.so / .dll)
add_library(mylib SHARED
src/lib.cpp
src/helper.cpp
)
# Header-only interface library
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE include)
# Link into executable
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
Note: For SHARED libraries, plan install/deploy rules (RUNTIME DESTINATION, etc.) so .so/.dll resolve at runtime.
PUBLIC vs PRIVATE vs INTERFACE
flowchart TB
subgraph LibA[Library A]
A_PRIVATE["PRIVATE headers e.g. src/internal/"]
A_PUBLIC["PUBLIC headers e.g. include/"]
end
subgraph LibB[Library B]
B_CODE[Lib B code]
end
subgraph App[Application]
APP_CODE[App code]
end
A_PRIVATE -.->|visible in| LibA
A_PUBLIC -->|visible in| LibA
A_PUBLIC -->|visible to| LibB
A_PUBLIC -->|visible to| App
LibB -->|links| LibA
App -->|links| LibB
style A_PRIVATE fill:#FFB6C1
style A_PUBLIC fill:#90EE90
# PRIVATE: this target only
target_include_directories(mylib PRIVATE src/internal)
# PUBLIC: this target and dependents
target_include_directories(mylib PUBLIC include)
# INTERFACE: dependents only (typical header-only)
target_include_directories(mylib INTERFACE include)
Example: Put src/internal/impl.h in PRIVATE if only mylib uses it; expose include/mylib/api.h as PUBLIC. After myapp links mylib, it sees only PUBLIC headers.
CMake Presets (3.19+)
What is a preset?
Problem: Different team members use different CMake commands.
# Developer A
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++ ..
# Developer B
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=clang++ -DENABLE_TESTS=ON ..
# CI/CD
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=......
Note: The method of copying and pasting a single long cmake line into a document is prone to breakage, so the purpose is to move it to the preset below.
SOLVED: Share build settings with CMakePresets.json.
CMakePresets.json basic structure
{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "20",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "debug",
"displayName": "Debug Build",
"description": "Debug build with sanitizers",
"inherits": "default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_CXX_FLAGS": "-fsanitize=address,undefined"
}
},
{
"name": "release",
"displayName": "Release Build",
"description": "Optimized release build",
"inherits": "default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "windows-msvc",
"displayName": "Windows MSVC",
"inherits": "default",
"generator": "Visual Studio 17 2022",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug",
"configuration": "Debug"
},
{
"name": "release",
"configurePreset": "release",
"configuration": "Release",
"jobs": 8
}
],
"testPresets": [
{
"name": "default",
"configurePreset": "debug",
"output": {
"outputOnFailure": true
}
}
]
}
How to use presets
# Check available presets
cmake --list-presets
# Configured with presets
cmake --preset=debug
# Build with presets
cmake --build --preset=debug
# Test with presets
ctest --preset=default
Practical preset example: multi-platform
{
"version": 6,
"configurePresets": [
{
"name": "linux-gcc",
"displayName": "Linux GCC",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/linux-gcc",
"cacheVariables": {
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_BUILD_TYPE": "Release"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux-clang",
"displayName": "Linux Clang",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/linux-clang",
"cacheVariables": {
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_BUILD_TYPE": "Release"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "macos",
"displayName": "macOS",
"generator": "Xcode",
"binaryDir": "${sourceDir}/build/macos",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "windows",
"displayName": "Windows MSVC",
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build/windows",
"architecture": "x64",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
}
]
}
Note: As you have more conditional presets, there will be confusion about “which preset is official” if you don’t pin the naming convention to your team document.
Utilizing presets in CI/CD
# .github/workflows/build.yml
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
preset: [debug, release]
steps:
- uses: actions/checkout@v3
- name: Install CMake
uses: lukka/get-cmake@latest
- name: Configure
run: cmake --preset=${{ matrix.preset }}
- name: Build
run: cmake --build --preset=${{ matrix.preset }}
- name: Test
run: ctest --preset=default
User-specific presets (CMakeUserPresets.json)
Team shared settings are written in CMakePresets.json and personal settings are written in CMakeUserPresets.json.
// CMakeUserPresets.json (add to git ignore)
{
"version": 6,
"configurePresets": [
{
"name": "my-dev",
"inherits": "debug",
"cacheVariables": {
"CMAKE_PREFIX_PATH": "/home/myuser/local",
"ENABLE_EXPERIMENTAL_FEATURES": "ON"
}
}
]
}
Note: It is better to place CMakeUserPresets.json in .gitignore, but also place an example file for team member onboarding (CMakeUserPresets.json.example).
Find external libraries: find_package
find_package Internal Mechanism:
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system) workflow:
1. Module mode search:
Search FindBoost.cmake in CMAKE_MODULE_PATH
→ Found /usr/share/cmake/Modules/FindBoost.cmake
→ Execute FindBoost.cmake
2. Config mode search (if Module fails):
Search in CMAKE_PREFIX_PATH, CMAKE_SYSTEM_PREFIX_PATH:
- <prefix>/lib/cmake/Boost/BoostConfig.cmake
- <prefix>/lib/cmake/boost-1.70.0/BoostConfig.cmake
- <prefix>/share/cmake/Boost/BoostConfig.cmake
- <prefix>/Boost*/BoostConfig.cmake
Common paths:
- Linux: /usr, /usr/local
- Windows: C:/Program Files/Boost
- macOS: /usr/local, /opt/homebrew
3. Parse BoostConfig.cmake:
- Check installed Boost version (≥1.70?)
- Search COMPONENTS (filesystem, system)
- Create IMPORTED targets:
* Boost::filesystem
* Boost::system
- Set properties for each target:
* INTERFACE_INCLUDE_DIRECTORIES (header paths)
* INTERFACE_LINK_LIBRARIES (link libraries)
* INTERFACE_COMPILE_DEFINITIONS (preprocessor macros)
4. REQUIRED option:
- Error immediately on failure, stop configuration
- Without option: only sets Boost_FOUND variable (conditional branching)
5. target_link_libraries(myapp PRIVATE Boost::filesystem):
- Propagate all Boost::filesystem properties to myapp
- Automatically add header paths (no target_include_directories needed)
- Automatically add link libraries (libboost_filesystem.a)
- Handle dependency chain (Boost::filesystem → Boost::system)
find_package Two Modes:
Module mode:
- Execute FindXxx.cmake (CMake-provided or project-provided)
- Variable-based: Xxx_FOUND, Xxx_INCLUDE_DIRS, Xxx_LIBRARIES
- Legacy approach, used for some packages only
Config mode (recommended):
- Execute XxxConfig.cmake (package-provided)
- IMPORTED target-based: Xxx::Xxx
- Modern approach, automatic property propagation
- Installed together with package
Boost example (Config mode)
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
# Internal operation:
# 1. Search and execute BoostConfig.cmake
# 2. Create IMPORTED targets:
# - Boost::filesystem (includes header paths, link libraries)
# - Boost::system
# 3. Set Boost_FOUND = TRUE
add_executable(myapp main.cpp)
# Link PRIVATE Boost::filesystem:
# - Only myapp uses Boost (not exposed)
# - Automatically add header path: -I/usr/include/boost
# - Automatically add link library: -lboost_filesystem -lboost_system
target_link_libraries(myapp PRIVATE Boost::filesystem Boost::system)
OpenSSL example (Module mode)
find_package(OpenSSL REQUIRED)
# Internal operation:
# 1. Execute FindOpenSSL.cmake (CMake-provided)
# 2. Search for OpenSSL on system:
# - Linux: /usr/lib/libssl.so, /usr/include/openssl
# - Windows: C:/Program Files/OpenSSL-Win64
# - macOS: /usr/local/opt/openssl@3
# 3. Create IMPORTED targets:
# - OpenSSL::SSL (libssl)
# - OpenSSL::Crypto (libcrypto)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)
Note: Mixing system OpenSSL and bundled OpenSSL may cause a runtime crash, so make sure to use the same build when deploying.
Handling when search fails
find_package(SomeLib)
if(SomeLib_FOUND)
target_link_libraries(myapp PRIVATE SomeLib::SomeLib)
else()
message(WARNING "SomeLib not found, using fallback")
endif()
Using pkg-config
find_package(PkgConfig REQUIRED)
pkg_check_modules(CURL REQUIRED libcurl)
target_link_libraries(myapp PRIVATE ${CURL_LIBRARIES})
target_include_directories(myapp PRIVATE ${CURL_INCLUDE_DIRS})
Note: Variable bases (CURL_LIBRARIES) have less propagation and tooling support than modern IMPORTED targets, so it is better to use the official CMake package or the PkgConfig::libcurl wrapper if possible.
Automatic download of dependencies with FetchContent (3.11+)
include(FetchContent)
# Automatic download of GoogleTest
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# Make it available
FetchContent_MakeAvailable(googletest)
# link
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE gtest_main)
Related article: Using vcpkg or Conan allows for more systematic package management.
3.24+ Enhancements: Remove timestamp warning with DOWNLOAD_EXTRACT_TIMESTAMP option.
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.0.0
DOWNLOAD_EXTRACT_TIMESTAMP ON # 3.24+
)
Note: FetchContent is network dependent, so you will need to decide whether to use it in conjunction with the SBOM/Security Scan process.
C++20 module support (3.28+)
What is a C++20 module?
Problems with existing header method:
- Parsing headers every time (slow compilation)
- Macro contamination
- Circular dependency problem C++20 module benefits:
- Compile only once (fast build)
- Explicit export (clean interface)
- Avoid circular dependencies
Using modules in CMake 3.28+
cmake_minimum_required(VERSION 3.28)
project(ModuleExample CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Enable module support
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
# Libraries (including modules)
add_library(mylib)
target_sources(mylib
PUBLIC
FILE_SET CXX_MODULES FILES
src/mymodule.cppm # Module interface file
PRIVATE
src/impl.cpp
)
# Run file
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
Module file example
// src/mymodule.cppm (module interface)
export module mymodule;
export namespace mylib {
int add(int a, int b);
int multiply(int a, int b);
}
// src/impl.cpp (module implementation)
module mymodule;
namespace mylib {
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
}
Note: Invalid module declaration in the implementation file will lead to link/visibility errors, so manage the interface and implementation pairs as a set.
// main.cpp (using module)
import mymodule;
#include <iostream>
int main() {
std::cout << mylib::add(2, 3) << std::endl; // 5
std::cout << mylib::multiply(4, 5) << std::endl; // 20
return 0;
}
Mixed use of module + header
add_library(mylib)
target_sources(mylib
PUBLIC
FILE_SET HEADERS FILES
include/legacy.h # legacy header
FILE_SET CXX_MODULES FILES
src/newmodule.cppm # new module
PRIVATE
src/legacy.cpp
src/newmodule.cpp
)
target_include_directories(mylib PUBLIC include)
Module support status by compiler (as of 2026)
| compiler | version | Module support |
|---|---|---|
| GCC | 14+ | Fully supported |
| Clang | 17+ | Fully supported |
| MSVC | 19.36+ (VS 2022) | Fully supported |
Build type and compilation options
Build type
# Set default values
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Flags for each build type
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
Note: The multi-configuration generator (Visual Studio, Xcode) selects --config instead of CMAKE_BUILD_TYPE, so do not confuse it with this pattern.
# Debug build
cmake -DCMAKE_BUILD_TYPE=Debug ..
# Release build
cmake -DCMAKE_BUILD_TYPE=Release ..
# Optimize including debug information
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
Target-specific compilation options
add_executable(myapp main.cpp)
# Enable warning
target_compile_options(myapp PRIVATE
-Wall -Wextra -Wpedantic
$<$<CONFIG:Debug>:-Werror> # Converts warnings to errors only in Debug.
)
# Preprocessing definitions
target_compile_definitions(myapp PRIVATE
APP_VERSION="1.0"
$<$<CONFIG:Debug>:DEBUG_MODE>
)
# header path
target_include_directories(myapp PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/generated
)
Generator Expression
# Branch by build type
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-O0 -g>
$<$<CONFIG:Release>:-O3>
)
# Compiler-specific branching
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-fno-exceptions>
$<$<CXX_COMPILER_ID:MSVC>:/EHsc>
)
Note: Generator expressions are strings, so debugging is difficult. If they become complicated, split target_compile_options into multiple lines or wrap it in a custom property to ensure readability.
Project structure and subdirectories
Recommended directory structure
project/
├── CMakeLists.txt # Root CMake settings
├── src/
│ ├── CMakeLists.txt # Source build settings
│ ├── main.cpp
│ └── lib/
│ ├── CMakeLists.txt
│ ├── lib.cpp
│ └── lib.h
├── include/ # public header
│ └── mylib/
│ └── api.h
├── tests/
│ ├── CMakeLists.txt
│ └── test_main.cpp
├── external/ # Submodule, external library
└── build/ # Build output (git ignore)
root CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# options
option(BUILD_TESTS "Build tests" ON)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
# subdirectory
add_subdirectory(src)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
src/CMakeLists.txt
# library
add_library(mylib
lib/lib.cpp
lib/lib.h
)
target_include_directories(mylib
PUBLIC ${CMAKE_SOURCE_DIR}/include
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)
# Run file
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
tests/CMakeLists.txt
find_package(GTest REQUIRED)
add_executable(tests
test_main.cpp
test_lib.cpp
)
target_link_libraries(tests PRIVATE
mylib
GTest::gtest
GTest::gtest_main
)
add_test(NAME MyTests COMMAND tests)
Complete example: multi-target project
Issue 1: Using absolute paths
Cause: Hardcoding absolute paths breaks builds in other environments.
# ❌ Incorrect use
target_include_directories(myapp PRIVATE /usr/local/include)
# ✅ Correct usage: Using CMake variables
target_include_directories(myapp PRIVATE ${CMAKE_SOURCE_DIR}/include)
Note: Always make sure you use source tree variables and not installed package paths.
Issue 2: Building from source directory
Cause: Running cmake . from the source directory causes the build files to get mixed up with the source.
# ❌ Incorrect use
cd /project
cmake .
# ✅ Correct usage: out-of-source build
mkdir build
cd build
cmake ..
Issue 3: Cache issues
Symptom: Changing CMake settings is not reflected.
Cause: Previous settings cached in CMakeCache.txt.
# Solution 1: Clear cache
rm CMakeCache.txt
cmake ..
# Solution 2: Recreate the build directory
cd ..
rm -rf build
mkdir build && cd build
cmake ..
Note: Please note any important -D flags or move them to a preset before deleting them.
Issue 4: Link order
Cause: If the library linking order is incorrect, an undefined reference error occurs.
# ❌ Wrong order: lib1 depends on lib2, but lib2 comes after
target_link_libraries(myapp PRIVATE lib2 lib1)
# ✅ Correct order: dependent libraries first
target_link_libraries(myapp PRIVATE lib1 lib2)
Issue 5: PUBLIC/PRIVATE confusion
Symptom: Header not found, or unnecessary header exposed.
# ❌ Misuse: Internal header as PUBLIC
target_include_directories(mylib PUBLIC src/internal)
# ✅ Correct use
target_include_directories(mylib
PUBLIC include # exposed to the outside world
PRIVATE src/internal # Internal use only
)
Issue 6: find_package fails
Symptom: Could not find package SomeLib.
Cause: The library is not installed or is in a location that CMake cannot find.
# Solution 1: Install package
sudo apt install libboost-dev # Linux
brew install boost # macOS
# Solution 2: Specify CMAKE_PREFIX_PATH
cmake -DCMAKE_PREFIX_PATH=/custom/install/path ..
# Solution 3: Specify the path manually
cmake -DBoost_ROOT=/usr/local/boost ..
Frequently occurring problems and solutions
Pattern 1: Compile warnings as errors
function(enable_strict_warnings target)
if(MSVC)
target_compile_options(${target} PRIVATE /W4 /WX)
else()
target_compile_options(${target} PRIVATE
-Wall -Wextra -Wpedantic -Werror
)
endif()
endfunction()
add_executable(myapp main.cpp)
enable_strict_warnings(myapp)
Pattern 2: Post-processing by build type
# Remove symbols with strip when building release
if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:myapp>
)
endif()
Pattern 3: Installation Rules
# Run file installation
install(TARGETS myapp
RUNTIME DESTINATION bin
)
# Install library
install(TARGETS mylib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
# Install header
install(DIRECTORY include/
DESTINATION include
)
# Install configuration file
install(FILES config.json
DESTINATION etc
)
# installation
cmake --build . --target install
# or
make install
Pattern 4: Generate version information
# version.h.in
#define APP_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define APP_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define APP_VERSION_PATCH @PROJECT_VERSION_PATCH@
# CMakeLists.txt
project(MyProject VERSION 1.2.3)
configure_file(version.h.in ${CMAKE_BINARY_DIR}/version.h)
target_include_directories(myapp PRIVATE ${CMAKE_BINARY_DIR})
Pattern 5: Conditional compilation
option(ENABLE_LOGGING "Enable logging" ON)
if(ENABLE_LOGGING)
target_compile_definitions(myapp PRIVATE ENABLE_LOGGING)
target_sources(myapp PRIVATE src/logger.cpp)
endif()
Pattern 6: Platform-specific sources
if(WIN32)
target_sources(myapp PRIVATE src/platform/windows.cpp)
elseif(APPLE)
target_sources(myapp PRIVATE src/platform/macos.cpp)
elseif(UNIX)
target_sources(myapp PRIVATE src/platform/linux.cpp)
endif()
Issue 6: find_package fails
Symptom: Could not find package SomeLib.
Cause: The library is not installed or is in a location that CMake cannot find.
# Solution 1: Install package
sudo apt install libboost-dev # Linux
brew install boost # macOS
# Solution 2: Specify CMAKE_PREFIX_PATH
cmake -DCMAKE_PREFIX_PATH=/custom/install/path ..
# Solution 3: Specify the path manually
cmake -DBoost_ROOT=/usr/local/boost ..
Issue 7: Presets not working
Symptom: “No such preset” error when running cmake --preset=debug.
Cause: CMake version is lower than 3.19, or JSON syntax error.
# Solution 1: Check CMake version
Requires cmake --version # 3.19 or higher
# Solution 2: JSON grammar verification
# Use VSCode or online JSON validator
# Solution 3: Check the preset list
cmake --list-presets
Performance Optimization
Parallel build
# Use all CPU cores
cmake --build . -j$(nproc)
# or make
make -j$(nproc)
Use Ninja
Ninja is a faster build system than Make.
# Create a build system with Ninja
cmake -G Ninja ..
# Build
ninja
Compilation caching with ccache
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()
Precompile Header (PCH)
target_precompile_headers(myapp PRIVATE
<iostream>
<vector>
<string>
)
Unity build (3.16+)
Reduce compilation time by merging multiple source files into one.
set(CMAKE_UNITY_BUILD ON)
set(CMAKE_UNITY_BUILD_BATCH_SIZE 16)
add_executable(myapp ${SOURCES})
Best practices/best practices
Target-based approach (Modern CMake)
# ❌ The old-fashioned way (global variables)
include_directories(include)
link_libraries(mylib)
add_definitions(-DDEBUG)
# ✅ Modern method (target-based)
target_include_directories(myapp PRIVATE include)
target_link_libraries(myapp PRIVATE mylib)
target_compile_definitions(myapp PRIVATE DEBUG)
Utilize Generator Expression
# Flags for each build type
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-O0 -g>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)
# Compiler-specific flags
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
$<$<CXX_COMPILER_ID:Clang>:-Weverything>
)
# Links by platform
target_link_libraries(myapp PRIVATE
$<$<PLATFORM_ID:Linux>:pthread>
$<$<PLATFORM_ID:Windows>:ws2_32>
)
Utilizing the interface library
# Common settings to interface library
add_library(common_settings INTERFACE)
target_compile_features(common_settings INTERFACE cxx_std_20)
target_compile_options(common_settings INTERFACE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# Apply to all targets
target_link_libraries(myapp PRIVATE common_settings)
target_link_libraries(mylib PRIVATE common_settings)
Automatic generation of version information
# version.h.in
#pragma once
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION "@PROJECT_VERSION@"
# CMakeLists.txt
project(MyProject VERSION 1.2.3)
configure_file(
${CMAKE_SOURCE_DIR}/version.h.in
${CMAKE_BINARY_DIR}/generated/version.h
@ONLY
)
target_include_directories(myapp PRIVATE ${CMAKE_BINARY_DIR}/generated)
Clarification of installation rules
# Run file installation
install(TARGETS myapp
RUNTIME DESTINATION bin
)
# Install library
install(TARGETS mylib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
# Install headers (using FILE_SET, 3.23+)
install(TARGETS mylib
FILE_SET HEADERS DESTINATION include
)
# Create Config file (can find_package from other projects)
install(EXPORT MyLibTargets
FILE MyLibTargets.cmake
NAMESPACE MyLib::
DESTINATION lib/cmake/MyLib
)
production pattern
Pattern 1: Conditional feature activation
option(ENABLE_LOGGING "Enable logging" ON)
option(ENABLE_PROFILING "Enable profiling" OFF)
option(BUILD_EXAMPLES "Build examples" OFF)
if(ENABLE_LOGGING)
target_compile_definitions(myapp PRIVATE ENABLE_LOGGING)
target_sources(myapp PRIVATE src/logger.cpp)
endif()
if(ENABLE_PROFILING)
target_link_libraries(myapp PRIVATE profiler)
endif()
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
Pattern 2: Platform-specific source management
# common source
set(COMMON_SOURCES
src/main.cpp
src/core.cpp
)
# Platform-specific sources
if(WIN32)
list(APPEND PLATFORM_SOURCES src/platform/windows.cpp)
elseif(APPLE)
list(APPEND PLATFORM_SOURCES src/platform/macos.cpp)
elseif(UNIX)
list(APPEND PLATFORM_SOURCES src/platform/linux.cpp)
endif()
add_executable(myapp ${COMMON_SOURCES} ${PLATFORM_SOURCES})
Pattern 3: Compile warnings as errors (CI/CD)
function(enable_warnings_as_errors target)
if(MSVC)
target_compile_options(${target} PRIVATE /W4 /WX)
else()
target_compile_options(${target} PRIVATE
-Wall -Wextra -Wpedantic -Werror
)
endif()
endfunction()
# Activated only in CI environment
if(DEFINED ENV{CI})
enable_warnings_as_errors(myapp)
endif()
Pattern 4: Post-processing by build type
# Strip when building Release
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND UNIX)
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:myapp>
COMMENT "Stripping binary"
)
endif()
# Print build information
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Build complete: $<TARGET_FILE:myapp>"
COMMAND ${CMAKE_COMMAND} -E echo "Build type: ${CMAKE_BUILD_TYPE}"
)
Pattern 5: Fix dependency versions
include(FetchContent)
# Manage with version variable
set(GOOGLETEST_VERSION "1.14.0")
set(FMT_VERSION "10.0.0")
set(SPDLOG_VERSION "1.12.0")
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v${GOOGLETEST_VERSION}
GIT_SHALLOW ON # Download only the latest commits
)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG ${FMT_VERSION}
GIT_SHALLOW ON
)
FetchContent_MakeAvailable(googletest fmt)
Pattern 6: Developer Mode
option(DEVELOPER_MODE "Enable developer mode" OFF)
if(DEVELOPER_MODE)
# Enable all warnings
add_compile_options(-Wall -Wextra -Wpedantic)
# Enable Sanitizer
add_compile_options(-fsanitize=address,undefined)
add_link_options(-fsanitize=address,undefined)
# Export compilation commands (used by clangd, ccls, etc.)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# test build
set(BUILD_TESTS ON)
endif()
Organize and Checklist
Summary of CMake key concepts
| concept | Description | Example |
|---|---|---|
| Target | Build artifacts (executable files, libraries) | add_executable, add_library |
| Attribute | Target-specific settings | target_include_directories |
| Dependency | Link relationship between targets | target_link_libraries |
| Preset | Reusable Build Settings | CMakePresets.json |
| Generator | Creating platform-specific build systems | Ninja, Make, Visual Studio |
CMake adoption checklist
# ✅ Default settings
- [ ] Install CMake 3.20 or higher (3.28+ recommended)
- [ ] Use out-of-source build (build/ directory)
- [ ] C++ standard settings (CMAKE_CXX_STANDARD)
- [ ] Set CMAKE_CXX_EXTENSIONS OFF
# ✅ Target management
- [ ] Target-based approach (add_executable, add_library)
- [ ] PUBLIC/PRIVATE/INTERFACE clearly distinguished
- Use [ ] target_* commands (avoid global commands)
# ✅ Dependency management
- Find external libraries with [ ] find_package
- [ ] Automatic download with FetchContent (optional)
- [ ] Version fixation (reproducible build)
# ✅ Build settings
- [ ] Create CMakePresets.json (shared with team)
- [ ] Build type setting (Debug/Release)
- [ ] Enable warning (-Wall -Wextra or /W4)
- [ ] Turn warnings into errors in CI/CD (-Werror)
# ✅ Test
- [ ] enable_testing() + add_test()
- [ ] CTest integration
# ✅ Installation and Deployment
- Create [ ] install() rule
- [ ] Config file creation (find_package support)
# ✅Performance
- [ ] Enable parallel builds (-j)
- [ ] Consider using Ninja
- [ ] ccache settings
- [ ] PCH utilization (big project)
# ✅ Latest features (optional)
- [ ] C++20 module support (3.28+)
- [ ] Use FILE_SET (3.23+)
- [ ] Unity build (3.16+)
Recommended features by version
| CMake version | Recommendations |
|---|---|
| 3.20+ | Basic features available |
| 3.23+ | Header management with FILE_SET |
| 3.25+ | Using LINUX variables |
| 3.28+ | C++20 module support |
Practical Tips: Using CMake Efficiently
- Maintain multiple build directories
# Maintain debug, release, and profiling simultaneously
cmake --preset=debug
cmake --preset=release
cmake --preset=profile
# Build each when needed
cmake --build build/debug
cmake --build build/release
- Export compile command (for LSP server)
# Create compile_commands.json (used by clangd, ccls, etc.)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Create a symbolic link at the root
# ln -s build/compile_commands.json .
- Measuring Build Time
# Measure build time
time cmake --build build
# or CMake built-in function
cmake --build build --verbose
- Selective build by target
# Build only specific targets (no full build required)
cmake --build build --target mylib
cmake --build build --target tests
# Build multiple targets simultaneously
cmake --build build --target mylib --target myapp
- Check cache variables
# Check current cache variable
cmake -L build
# Check advanced variables
cmake -LA build
# Check specific variables
cmake -L build | grep CMAKE_CXX_COMPILER
Quick reference: CMake command cheat sheet
# 🏗️ Project settings
cmake -S . -B build # source: current, build: build/
cmake --preset=debug # Use preset (3.19+)
cmake -DCMAKE_BUILD_TYPE=Release # Specify build type
# 🔨Build
cmake --build build # Run build
cmake --build build -j8 # parallel build (8 cores)
cmake --build build --target myapp # specific target only
# 🧪Test
ctest --test-dir build # Run tests
ctest --preset=default # Test with preset
# 📦 Install
cmake --install build # Run installation
cmake --install build --prefix /usr/local # Specify path
# 🧹 Organization
cmake --build build --target clean # Delete build results
rm -rf build # complete initialization
Troubleshooting: Quick problem solving
| Symptoms | Cause | Solution |
|---|---|---|
| Could not find package | Library not installed | Specify apt install, brew install, or CMAKE_PREFIX_PATH |
| Undefined reference | Link order error | Check target_link_libraries order |
| Header not found | Missing include path | add target_include_directories |
| Cache issues | CMakeCache.txt Outdated | rm CMakeCache.txt or cmake --fresh |
| No presets | CMake version low | CMake 3.19+ upgrade |
| Slow build | Use Make | Switch to Ninja (-G Ninja) |
Build system performance comparison
| build system | speed | Parallelization | cross platform | learning curve |
|---|---|---|---|---|
| Make | Normal | LIMITED | Linux/macOS | Easy |
| Ninja | Fast | Excellent | All platforms | Easy |
| Visual Studio | Normal | Excellent | Windows | Easy |
| Xcode | Normal | Excellent | macOS | middle |
Recommended: Ninja (fastest and supports all platforms)
Next steps
- Detailed study of target properties in CMake Targets Advanced
- In-depth dependency management in CMake find_package: CONFIG vs MODULE, Search Paths and Errors
- CMake vs other tools in Build System Comparison
- Learn how to use the package manager in vcpkg basics
- Learn about another package manager in Conan Basics
FAQ
Q1: What is the difference between CMake and Make?
A: CMake is a tool that creates a build system (meta build system), and Make is a tool that performs the build. When CMake creates a Makefile, Make reads that Makefile and compiles it.
CMakeLists.txt → [CMake] → Makefile → [Make] → Executable file
Q2: What is out-of-source build?
A: Separate source and build directories. If you create a build/ directory and run cmake .. inside it, the build files will be generated only in build/, keeping your source clean.
# ✅ Correct way
mkdir build && cd build
cmake ..
# ❌ Wrong method (mixing source and build files)
cmake .
Q3: When should I use PUBLIC vs PRIVATE vs INTERFACE?
A:
- PRIVATE: only used inside this target (implementation detail)
- PUBLIC: Use this target + targets linking this target as well (public API)
- INTERFACE: Use only targets that link to this target (header-only library)
# internal header
target_include_directories(mylib PRIVATE src/internal)
# public header
target_include_directories(mylib PUBLIC include)
# Header-only interface library
target_include_directories(header_only INTERFACE include)
Q4: When to use CMakePresets.json?
A: Used when sharing build settings in a team project or CI/CD. Personal settings are written in CMakeUserPresets.json and added to git ignore.
# Without presets (complicated)
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++ -DENABLE_TESTS=ON ..
# Use presets (simple)
cmake --preset=debug
Q5: What if find_package fails?
A:
- Verify library installation
- Specify
CMAKE_PREFIX_PATH - Use package manager (vcpkg, conan)
# 1. Installation
sudo apt install libboost-dev # Linux
brew install boost # macOS
# 2. Specify the route
cmake -DCMAKE_PREFIX_PATH=/usr/local ..
# 3. Use vcpkg
cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg.cmake ..
Q6: Do I need to use the C++20 module?
A: As of 2026, all major compilers (GCC 14+, Clang 17+, MSVC 19.36+) support it and its use is recommended for new projects. Migrate existing projects gradually. Advantages: Fast compilation, clean interface, avoids circular dependencies. Disadvantage: Increased build system complexity, caution required when mixed with legacy code
Q7: The build is slow. How do I optimize it?
A:
- Using Ninja (
cmake -G Ninja) - Parallel build (
cmake --build . -j$(nproc)) - Enable ccache
- Use PCH (precompile header)
- Consider Unity Build (large projects)
# enable ccache
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()
# Unity build
set(CMAKE_UNITY_BUILD ON)
Q8: What are CMake learning resources?
A:
- CMake official documentation (Check the latest features)
- “Professional CMake: A Practical Guide” (practical patterns)
- CMake Tutorial (step-by-step learning)
- Effective Modern CMake (Best Practice)
Q9: How to migrate an existing Makefile project to CMake?
A: Migrate gradually, step by step.
# Step 1: Create a minimal CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyProject)
file(GLOB_RECURSE SOURCES "src/*.cpp") # Temporarily use GLOB
add_executable(myapp ${SOURCES})
# Step 2: Explicitly list source files
set(SOURCES
src/main.cpp
src/module1.cpp
# ...
)
# Step 3: Separate by target
add_library(mylib src/lib.cpp)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# Step 4: Structuring subdirectories
add_subdirectory(src)
add_subdirectory(tests)
Q10: How to completely reset the CMake cache?
A:
# Method 1: Delete the build directory (most obvious)
rm -rf build
mkdir build && cd build
cmake ..
# Method 2: Delete only CMakeCache.txt
rm CMakeCache.txt
cmake ..
# Method 3: Initialization with CMake command
cmake --fresh ... # CMake 3.24+
Q11: How to maintain multiple build types simultaneously?
A: Separate build directories or use presets.
# Method 1: Separate directories
mkdir build-debug && cd build-debug
cmake -DCMAKE_BUILD_TYPE=Debug ..
mkdir build-release && cd build-release
cmake -DCMAKE_BUILD_TYPE=Release ..
# Method 2: Use presets (recommended)
cmake --preset=debug
cmake --preset=release
# Generated in build/debug and build/release respectively
Q12: How to include Git commit hashes in versions in CMake?
A:
# Get Git commit hash
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Create version header
configure_file(
${CMAKE_SOURCE_DIR}/version.h.in
${CMAKE_BINARY_DIR}/version.h
)
# version.h.in
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
#define GIT_COMMIT "@GIT_COMMIT_HASH@"
CMake 3.28+ allows you to efficiently build cross-platform projects including C++20 modules. Share team settings and take advantage of the latest features to increase productivity with CMakePresets.json.
Good article to read together (internal link)
- CMake Targets in C++
- C++ CMake find_package complete guide | External library integration
- C++ Conan Basics — Install, conanfile, Profiles, CMake
- vcpkg for Reproducible C++ Builds: Manifest Mode, Triplets, Versioning and Overlay Ports
- Complete comparison of C++ build systems | CMake·Meson·Bazel·Makefile·Package Manager Selection Guide
Related articles
- C++ CMake find_package complete guide | External library integration
- CMake Targets in C++
- C++ Conan Basics — Install, conanfile, Profiles, CMake
- C++ Makefile
- C++ Benchmarking