Dominik Berner

C++ Coder, Agilist, Rock Climber

Project maintained by bernedom Read the privacy statement for this blog

name: title layout: true class: center, middle, inverse

CMake Best Practices

Dominik Berner

CMake Logo



  • ask about experience with CMake
  • Typical struggles, or concrete problems
  • Tiniest of tiny introductions to CMake, then a few good practices and principles and then some nifty tricks

CMake in a nutshell


  • CMake is a build-system generator or meta-build-system
  • Inputs: source files, compiler-toolchain, build-environments and logical build targets
  • Outputs: instructions for build-systems (make, ninja, visual studio, xcode…)
  • Additional features include running tests, packaging, installing and custom commands like static code analysis etc. ]

CMake configuration process in a nutshell

A typical CMakeLists.txt


project(myProject VERSION 1.0.0 LANGUAGES CXX)

find_package(OpenSSL REQUIRED COMPONENTS Crypto)

add_library(myLib lib.cpp)

add_executable(myApp main.cpp)

target_link_libraries(myApp PRIVATE myLib OpenSSL::Crypto)



define a project, find dependencies, add targets, link targets

CMake Best Practices - The Book

.left-column[ CMake Best Practices Cover ] .right-column[ .left[ Get it from packt:

CMake Best Practices URL ] ] —

Overall Good Practice


  1. Platform-agnostic CMakeLists.txt
  2. presets > toolchain files > command line > env variables.
  3. Use target_* commands over global ones.
  4. package managers > FetchContent > ExternalProject > add_subdirectory
  5. Leave decisions for SHARED or STATIC libraries to the user for library projects.
  6. include for modules, add_subdirectory for subprojects.
  7. Make building of tests optional.
  8. Frequently update CMake to the newest version.
  9. add_exectuable(${PROJECT_NAME})



Compiler options: mandatory ones -> toolchain optional ones such as -Wall etc presets


Structure of a CMake Project


├── cmake
│   ├── MyModule.cmake
│   └── myTarget_sources.cmake # optional sources lists
*├── CMakeLists.txt #project, targets, dependencies, install & package instructions
├── doc
├── include  #for libraries only
│   └── projectname
│       └── public_header.h
├── src
│   └── code.cpp
├── test
*│   ├── CMakeLists.txt # Test targets, test-only dependencies 
│   └── src
│       └── test.cpp
└── CMakePresets.json


Platform agnostic custom commands


  • Use cmake -E to execute platform agnostic commands
    • cmake -E <command>
    • copy stuff, hashes, comparing files, etc
  • For more complex ones use CMake in script mode
    • cmake -P someScript.cmake ]

CMake Presets

“CMake presets (From 3.17) are the best thing since introduction of targets” - “CMake Best Practice”


  • Presets define configuration settings for CMake
  • Presets are stored in a CMakePresets.json file -> Put this into git
    • CMakeUserPresets.json for user-specific settings -> Do not version this
  • Configure presets are the most common
    • Use build presets for multi-config generators
  • Aggregate and inherit (invisible) presets
  • Use the override mechanism to change settings
  • use $env{HOME} and variable expansion (i.e. ${sourceDir} ]


  • Who knows Presets?

CMake Presets - Configure example


    "name": "ci-ninja-linux-debug",
    "displayName": "Ninja Linux Debug",
    "inherits": [
    "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug"
    "generator": "Ninja",
    "binaryDir": "${sourceDir}/build"


Presets - Organisation

CMake Presets

.left[ Examples:

  • ci-linux-clang12-x86_64-debug
  • ci-linux-gcc10-x86_64-release
  • dev-linux-release -> No compiler specified, use system default
  • ci-windows-msvc2019-x86_64 -> No build type specified, use build preset ]

Symbol Visibility in Libraries (DLLs)


add_library(hello SHARED) # Prefer to let the user set SHARED or STATIC
*set_property(TARGET hello PROPERTY CXX_VISIBILITY_PRESET "hidden")


*generate_export_header(hello EXPORT_FILE_NAME export/hello/export_hello.hpp)

*target_include_directories(hello PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/export")



  • Don’t forget to add the generated header to the install instructions!

Large Codebases over multiple repos


  • Use FetchContent to fetch centralized scripts, macros and presets
  • Use a package manager for 3rd-Party dependencies
  • Use build-containers and toolchain files for defining (cross-compilation) toolchains
  • Make subprojects build standalone
  • Use ExternalProject for non-CMake projects (i.e. autotools)
    • Use FetchContent for CMake projects
  • CI runs should trigger downstream builds frequently ] —

Superbuilds With Non-CMake parts


set(installDir ${CMAKE_CURRENT_BINARY_DIR}/install)

  INSTALL_DIR ${installDir}
*  BUILD_COMMAND make -j # Use find_program to find make first 

  INSTALL_DIR ${installDir}

*ExternalProject_Add_StepDependencies(someDep2 configure AnAutoToolsProject)



The configure step of somedep depends on the completion of AnAutoTOolsProject

Warning, ExternalProject happens during configuration time

Speeding up compilation - ccache



find_program(CCACHE_PROGRAM ccache)


  "name": "ccache-env",
  "hidden": true,
  "environment": {
      "CCACHE_BASEDIR": "${sourceDir}",
      "CCACHE_SLOPPINESS": "pch_defines,time_macros"


Speeding up compilation - Precompiled Headers


target_precompile_headers(ch14_precompiled_headers PRIVATE 
  • Generates a cmake_pch.hxx.gch in the build dir
  • it is automatically included in all .cpp files
  • automatically recompiled if the header changes
    • Disclaimer: It does not always work if there are external dependencies included. ]

Speeding up compilation - Unity Builds



target_sources(ch14_unity_build PRIVATE 

# Enable unity build for the target
*set_target_properties(ch14_unity_build PROPERTIES UNITY_BUILD True)

# exclude eratosthenes.cpp from the unity build
set_source_files_properties(src/eratosthenes.cpp PROPERTIES
                          SKIP_UNITY_BUILD_INCLUSION YES)


Speeding up compilation - Unity Builds

.left[ A file called unity_0_cxx.cxx is generated in the build directory

  /* generated by CMake */

#include "/workspaces/CMake-Best-Practices/chapter14/unity_build/src/main.cpp"

#include "/workspaces/CMake-Best-Practices/chapter14/unity_build/src/fibonacci.cpp"


Speeding up CMake - profiling


cmake -S <sourceDir> -B <buildDir> \ 
    --profiling-output ./profiling.json --profiling-format=google-trace


CMake profiling output

Your turn - Questions?

C++ Coder, Agilist & Rock Climber

.left-column[ Me ] .right-column[ .left[ web

twitter @BernerDominik

github bernedom

mail ] ]



book plug 1 slide

  • Presets - possible a bigger one by itself - OK
    • CMakePresets.json and CMAkeUserPrestes.json
    • Presets and cross compiling using toolchains
    • inheriting
  • Cmake in script mode for platform independent commands -OK
  • Test orchestration
    • test grouping/test parallization
    • test timeouts
    • test ressources
    • defining fixtures
    • Running tests in an emulator when cross compiling
  • speeding up cmake
    • cmake profiling - OK
    • unity builds
    • precompiled headers
    • ccache
  • CMake dependency graphs -OK
  • DLL Symbol visibility
    • SHARED STATIC defined by preset -OK
  • Dependency management find_package to rule them all
    • Upstream over find_package over FetchContent/ExternalProject

Small but useful

  • export compile commands
  • No ${PROJECT_NAME} targets - semantically different meaning - Kill the thing - OK

  • Generator expressions - Explained

  • custom commands / custom targets dependent on each other

  • ccache
  • sanitizers etc
    • Custom build types
    • Code coverage
    • static code analysis (iwyu)
  • Search order for sysroots (setting BOTH etc) - individual programs
  • toolchain checks (very advanced) - NO

  • Packaging and installing
  • Q&A

  • ccmake and cmake-gui (console yay)
    • Tweaking environment variables
  • conan integration instead of fetch content

Keep the overview with dependency graphs

cmake /path/to/build/dir

CMake dependency graph