CMake Cheatsheet
Überblick
CMake ist ein plattformunabhängiger Systemgenerator, der plattformunabhängige Konfigurationsdateien verwendet, um native Build-Tooldateien zu generieren (Makefiles, Visual Studio-Projekte, Xcode-Projekte, etc.).
Installation
Paketmanager
# Ubuntu/Debian
sudo apt install cmake
# macOS
brew install cmake
# CentOS/RHEL
sudo yum install cmake
# Windows (Chocolatey)
choco install cmake
# From source
wget https://cmake.org/files/v3.25/cmake-3.25.0.tar.gz
tar -xzf cmake-3.25.0.tar.gz
cd cmake-3.25.0 && ./bootstrap && make && sudo make install
```_
## Grundkonzepte
### Schlüsselbegriffe
CMakeLists.txt # Configuration file Target # Executable, library, or custom target Generator # Tool that creates build files Cache # Stored configuration variables Out-of-source # Build directory separate from source
### Projektstruktur
project/ ├── CMakeLists.txt ├── src/ │ ├── main.cpp │ └── utils.cpp ├── include/ │ └── utils.h └── build/ # Out-of-source build directory ```_
Basic CMakeLists.txt
Minimales Beispiel
```cmake cmake_minimum_required(VERSION 3.10) project(MyProject)
set(CMAKE_CXX_STANDARD 17)
add_executable(myapp main.cpp) ```_
Einfache Bibliothek
```cmake cmake_minimum_required(VERSION 3.10) project(MyProject VERSION 1.0.0)
Set C++ standard
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
Add library
add_library(mylib utils.cpp network.cpp)
Add executable
add_executable(myapp main.cpp)
Link library to executable
target_link_libraries(myapp mylib) ```_
Variablen
Einbauvariablen
```cmake
Project information
$\\{PROJECT_NAME\\} $\\{PROJECT_VERSION\\} $\\{PROJECT_SOURCE_DIR\\} $\\{PROJECT_BINARY_DIR\\}
System information
$\\{CMAKE_SYSTEM_NAME\\} # Linux, Windows, Darwin $\\{CMAKE_SYSTEM_VERSION\\} $\\{CMAKE_SYSTEM_PROCESSOR\\}
Compiler information
$\\{CMAKE_CXX_COMPILER\\} $\\{CMAKE_CXX_COMPILER_ID\\} # GNU, Clang, MSVC $\\{CMAKE_CXX_COMPILER_VERSION\\}
Build information
$\\{CMAKE_BUILD_TYPE\\} # Debug, Release, RelWithDebInfo $\\{CMAKE_CURRENT_SOURCE_DIR\\} $\\{CMAKE_CURRENT_BINARY_DIR\\} ```_
Einstellvariablen
```cmake
Set variable
set(SOURCES main.cpp utils.cpp) set(MY_VAR "value")
List operations
list(APPEND SOURCES network.cpp) list(REMOVE_ITEM SOURCES main.cpp) list(LENGTH SOURCES NUM_SOURCES)
String operations
string(TOUPPER $\\{PROJECT_NAME\\} PROJECT_NAME_UPPER) string(REPLACE "old" "new" NEW_STRING $\\{OLD_STRING\\})
Cache variables
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries") ```_
Ziele
Ausführbare
```cmake
Simple executable
add_executable(myapp main.cpp)
Executable with multiple sources
add_executable(myapp main.cpp utils.cpp network.cpp )
Executable with variable sources
set(SOURCES main.cpp utils.cpp) add_executable(myapp $\\{SOURCES\\}) ```_
Bibliotheken
```cmake
Static library
add_library(mylib STATIC utils.cpp network.cpp)
Shared library
add_library(mylib SHARED utils.cpp network.cpp)
Header-only library
add_library(mylib INTERFACE) target_include_directories(mylib INTERFACE include/)
Object library
add_library(mylib OBJECT utils.cpp network.cpp)
add_executable(myapp main.cpp $
Zieleigenschaften
```cmake
Set target properties
set_target_properties(myapp PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON OUTPUT_NAME "my_application" )
Include directories
target_include_directories(myapp PRIVATE include/ PUBLIC $\\{CMAKE_CURRENT_SOURCE_DIR\\}/public_include/ )
Compile definitions
target_compile_definitions(myapp PRIVATE DEBUG_MODE PUBLIC API_VERSION=2 )
Compile options
target_compile_options(myapp PRIVATE -Wall -Wextra PUBLIC -fPIC )
Link libraries
target_link_libraries(myapp PRIVATE mylib PUBLIC pthread ) ```_
Finden von Paketen
find_packing
```cmake
Find required package
find_package(Threads REQUIRED) target_link_libraries(myapp Threads::Threads)
Find optional package
find_package(OpenSSL) if(OpenSSL_FOUND) target_link_libraries(myapp OpenSSL::SSL OpenSSL::Crypto) target_compile_definitions(myapp PRIVATE HAVE_OPENSSL) endif()
Find package with components
find_package(Boost REQUIRED COMPONENTS system filesystem) target_link_libraries(myapp Boost::system Boost::filesystem)
Find package with version
find_package(Qt5 5.10 REQUIRED COMPONENTS Core Widgets) target_link_libraries(myapp Qt5::Core Qt5::Widgets) ```_
pkg-config
```cmake find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_include_directories(myapp PRIVATE $\\{GTK3_INCLUDE_DIRS\\}) target_link_libraries(myapp $\\{GTK3_LIBRARIES\\}) target_compile_options(myapp PRIVATE $\\{GTK3_CFLAGS_OTHER\\}) ```_
Bedingte Logik
wenn/else/endif
```cmake if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(myapp PRIVATE DEBUG_MODE) elseif(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_definitions(myapp PRIVATE RELEASE_MODE) endif()
Platform-specific code
if(WIN32) target_sources(myapp PRIVATE windows_specific.cpp) elseif(UNIX) target_sources(myapp PRIVATE unix_specific.cpp) endif()
Compiler-specific options
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(myapp PRIVATE -Wall -Wextra) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(myapp PRIVATE /W4) endif() ```_
Option
```cmake option(BUILD_TESTS "Build test programs" ON) option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
if(BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() ```_
Funktionen und Makros
Funktionen
```cmake function(add_my_executable name) add_executable($\\{name\\} $\\{ARGN\\}) target_compile_features($\\{name\\} PRIVATE cxx_std_17) target_compile_options($\\{name\\} PRIVATE -Wall -Wextra) endfunction()
Usage
add_my_executable(myapp main.cpp utils.cpp) ```_
Macros
```cmake macro(set_default_build_type) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) endif() endmacro()
Usage
set_default_build_type() ```_
Erweiterte Funktionen
```cmake function(add_library_with_alias target_name) set(options SHARED STATIC) set(oneValueArgs ALIAS) set(multiValueArgs SOURCES HEADERS)
cmake_parse_arguments(ARG "$\\\\{options\\\\}" "$\\\\{oneValueArgs\\\\}" "$\\\\{multiValueArgs\\\\}" $\\\\{ARGN\\\\})
if(ARG_SHARED)
add_library($\\\\{target_name\\\\} SHARED $\\\\{ARG_SOURCES\\\\})
else()
add_library($\\\\{target_name\\\\} STATIC $\\\\{ARG_SOURCES\\\\})
endif()
if(ARG_ALIAS)
add_library($\\\\{ARG_ALIAS\\\\} ALIAS $\\\\{target_name\\\\})
endif()
endfunction()
Usage
add_library_with_alias(mylib SHARED ALIAS MyNamespace::mylib SOURCES utils.cpp network.cpp ) ```_
Prüfung
CTest Integration
```cmake enable_testing()
Add test executable
add_executable(test_utils test_utils.cpp) target_link_libraries(test_utils mylib)
Add test
add_test(NAME test_utils COMMAND test_utils)
Test with arguments
add_test(NAME test_with_args COMMAND myapp --test --verbose)
Test properties
set_tests_properties(test_utils PROPERTIES TIMEOUT 30 WORKING_DIRECTORY $\\{CMAKE_CURRENT_BINARY_DIR\\} ) ```_
Google Test Integration
```cmake find_package(GTest REQUIRED)
add_executable(unit_tests test_main.cpp test_utils.cpp )
target_link_libraries(unit_tests mylib GTest::GTest GTest::Main )
Discover tests automatically
include(GoogleTest) gtest_discover_tests(unit_tests) ```_
Installation
Einfache Installation
```cmake
Install executable
install(TARGETS myapp RUNTIME DESTINATION bin )
Install library
install(TARGETS mylib LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin )
Install headers
install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.h" )
Install files
install(FILES config.txt DESTINATION etc ) ```_
Paketkonfiguration
```cmake
Generate package config files
include(CMakePackageConfigHelpers)
Create config file
configure_package_config_file( $\\{CMAKE_CURRENT_SOURCE_DIR\\}/Config.cmake.in $\\{CMAKE_CURRENT_BINARY_DIR\\}/MyProjectConfig.cmake INSTALL_DESTINATION lib/cmake/MyProject )
Create version file
write_basic_package_version_file( $\\{CMAKE_CURRENT_BINARY_DIR\\}/MyProjectConfigVersion.cmake VERSION $\\{PROJECT_VERSION\\} COMPATIBILITY SameMajorVersion )
Install config files
install(FILES $\\{CMAKE_CURRENT_BINARY_DIR\\}/MyProjectConfig.cmake $\\{CMAKE_CURRENT_BINARY_DIR\\}/MyProjectConfigVersion.cmake DESTINATION lib/cmake/MyProject )
Export targets
install(EXPORT MyProjectTargets FILE MyProjectTargets.cmake NAMESPACE MyProject:: DESTINATION lib/cmake/MyProject ) ```_
Generator-Ausdrücke
Ausdrücke des Basisgenerators
```cmake
Build type specific
target_compile_definitions(myapp PRIVATE
$<$
Platform specific
target_compile_definitions(myapp PRIVATE
$<$
Compiler specific
target_compile_options(myapp PRIVATE
$<$
Erweiterte Generatorausdrücke
```cmake
Target properties
target_include_directories(myapp PRIVATE
$
Conditional linking
target_link_libraries(myapp
$<$
File operations
target_sources(myapp PRIVATE
$<$
Verwendung der Befehlszeile
Grundlegende Befehle
```bash
Configure (generate build files)
cmake -S . -B build
Build
cmake --build build
Install
cmake --install build
Test
cd build && ctest
Clean
cmake --build build --target clean ```_
Konfigurationsoptionen
```bash
Set build type
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
Set variables
cmake -S . -B build -DBUILD_TESTS=ON -DBUILD_SHARED_LIBS=ON
Set generator
cmake -S . -B build -G "Unix Makefiles" cmake -S . -B build -G "Ninja" cmake -S . -B build -G "Visual Studio 16 2019"
Set toolchain
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
Parallel build
cmake --build build --parallel 4 cmake --build build -j 4 ```_
Erweiterte Nutzung
```bash
Configure with preset
cmake --preset=default
Build specific target
cmake --build build --target myapp
Install to custom prefix
cmake --install build --prefix /opt/myapp
Run specific test
cd build && ctest -R test_utils
Verbose output
cmake --build build --verbose ctest --verbose ```_
Langfristige Entwicklung
Plattform-Detektion
cmake
if(WIN32)
# Windows-specific code
target_compile_definitions(myapp PRIVATE PLATFORM_WINDOWS)
elseif(APPLE)
# macOS-specific code
target_compile_definitions(myapp PRIVATE PLATFORM_MACOS)
elseif(UNIX)
# Linux/Unix-specific code
target_compile_definitions(myapp PRIVATE PLATFORM_LINUX)
endif()
_
Toolchain Dateien
```cmake
toolchain-mingw.cmake
set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Usage
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=toolchain-mingw.cmake ```_
Best Practices
Projektstruktur
```cmake cmake_minimum_required(VERSION 3.15)
project(MyProject VERSION 1.0.0 DESCRIPTION "My awesome project" LANGUAGES CXX )
Only do these if this is the main project
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(CMAKE_CXX_EXTENSIONS OFF) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
include(CTest)
if(BUILD_TESTING)
add_subdirectory(tests)
endif()
endif()
Add subdirectories
add_subdirectory(src) add_subdirectory(external) ```_
Moderne CMake Muster
```cmake
Use target-based approach
target_include_directories(mylib PUBLIC
$
Use imported targets
find_package(Threads REQUIRED) target_link_libraries(myapp PRIVATE Threads::Threads)
Use generator expressions
target_compile_features(myapp PRIVATE cxx_std_17)
Avoid global commands
Don't use: include_directories(), link_directories(), add_definitions()
Use target-specific commands instead
```_
Fehlerbehebung
```cmake
Check CMake version
cmake_minimum_required(VERSION 3.15)
Validate variables
if(NOT DEFINED PROJECT_VERSION) message(FATAL_ERROR "PROJECT_VERSION must be defined") endif()
Check for required files
if(NOT EXISTS "$\\{CMAKE_CURRENT_SOURCE_DIR\\}/src/main.cpp") message(FATAL_ERROR "main.cpp not found") endif()
Validate options
| if(BUILD_TYPE AND NOT BUILD_TYPE MATCHES "^(Debug | Release | RelWithDebInfo | MinSizeRel)$") | message(FATAL_ERROR "Invalid BUILD_TYPE: $\\{BUILD_TYPE\\}") endif() ```_
Debugging
Debütausgabe
```cmake
Print variables
message(STATUS "CMAKE_BUILD_TYPE: $\\{CMAKE_BUILD_TYPE\\}") message(STATUS "CMAKE_CXX_COMPILER: $\\{CMAKE_CXX_COMPILER\\}")
Print all variables
get_cmake_property(_variableNames VARIABLES) foreach(_variableName $\\{_variableNames\\}) message(STATUS "$\\{_variableName\\}=$\\{$\\{_variableName\\}\\}") endforeach()
Print target properties
get_target_property(MYAPP_SOURCES myapp SOURCES) message(STATUS "myapp sources: $\\{MYAPP_SOURCES\\}") ```_
Fehlerbehebung
```bash
Verbose makefile
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON
Debug find_package
cmake -S . -B build --debug-find
Trace execution
cmake -S . -B build --trace
Debug output
cmake -S . -B build --debug-output ```_
Ressourcen
- Offizielle Dokumentation: [cmake.org/documentation](https://__LINK_4___
- CMake Tutorial: [cmake.org/cmake/help/latest/guide/tutorial](_LINK_4__
- *Moderne CMake: cliutils.gitlab.io/modern-cmake
- CMake Beispiele: [github.com/ttroy50/cmake-Beispiele](LINK_4