Aller au contenu

Faire de la feuille de chaleur

Aperçu général

CMake est un générateur de système de construction multiplateforme qui utilise des fichiers de configuration indépendants de la plate-forme pour générer des fichiers d'outils de construction native (Makefiles, projets Visual Studio, projets Xcode, etc.).

Installation

Gestionnaires de paquets

# 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

Concepts de base

Termes clés

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
```_

### Structure du projet

project/ ├── CMakeLists.txt ├── src/ │ ├── main.cpp │ └── utils.cpp ├── include/ │ └── utils.h └── build/ # Out-of-source build directory


## C MakeLists.txt de base

### Exemple minimal
```cmake
cmake_minimum_required(VERSION 3.10)
project(MyProject)

set(CMAKE_CXX_STANDARD 17)

add_executable(myapp main.cpp)

Bibliothèque simple

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)

Variables

Variables intégrées

# 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\\\\}

Réglage des variables

# 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")

Objectifs

exécutables

# 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\\\\})

Bibliothèques

# 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 $<TARGET_OBJECTS:mylib>)

Propriétés des cibles

# 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
)

Trouver des paquets

find_package

# 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

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\\\\})

Logique conditionnelle

Si/encore/endif

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

option(BUILD_TESTS "Build test programs" ON)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)

if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

Fonctions et Macros

Fonctions

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

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()

Fonctions avancées

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
)

Essais

Intégration CTest

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\\\\}
)

Intégration des tests Google

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

Installation de base

# 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
)

Configuration du paquet

# 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
)

Expressions de générateurs

Expressions de base des générateurs

# Build type specific
target_compile_definitions(myapp PRIVATE
    $<$<CONFIG:Debug>:DEBUG_MODE>
    $<$<CONFIG:Release>:RELEASE_MODE>
)

# Platform specific
target_compile_definitions(myapp PRIVATE
    $<$<PLATFORM_ID:Windows>:WINDOWS_BUILD>
    $<$<PLATFORM_ID:Linux>:LINUX_BUILD>
)

# Compiler specific
target_compile_options(myapp PRIVATE
    $<$<CXX_COMPILER_ID:GNU>:-Wall>
    $<$<CXX_COMPILER_ID:MSVC>:/W4>
)

Expressions avancées du générateur

# Target properties
target_include_directories(myapp PRIVATE
    $<TARGET_PROPERTY:mylib,INTERFACE_INCLUDE_DIRECTORIES>
)

# Conditional linking
target_link_libraries(myapp
    $<$<BOOL:$\\\\{BUILD_SHARED_LIBS\\\\}>:$\\\\{CMAKE_DL_LIBS\\\\}>
)

# File operations
target_sources(myapp PRIVATE
    $<$<PLATFORM_ID:Windows>:windows_main.cpp>
    $<$<PLATFORM_ID:Linux>:linux_main.cpp>
)

Utilisation de la ligne de commande

Commandes de base

# 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

Options de configuration

# 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

Utilisation avancée

# 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

Développement transversal

Détection des plates-formes

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()

Fichiers de la chaîne d'outils

# 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

Meilleures pratiques

Structure du projet

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)

Modèles de fabrication modernes

# Use target-based approach
target_include_directories(mylib PUBLIC
    $<BUILD_INTERFACE:$\\\\{CMAKE_CURRENT_SOURCE_DIR\\\\}/include>
    $<INSTALL_INTERFACE:include>
)

# 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

Gestion des erreurs

# 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()

Déboguement

Sortie de débogage

# 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\\\\}")

Dépannage

# 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

Ressources