Systèmes de build : CMake et gestion des paquets
Maîtrisez les bases de CMake, le CMake moderne orienté cibles, FetchContent pour les dépendances, les gestionnaires de paquets vcpkg et Conan, ainsi que les options du compilateur pour des builds sûrs et optimisés.
Pourquoi les systèmes de build sont importants
Dès qu'un projet C++ dépasse un seul fichier, il vous faut un système de build pour gérer la compilation, l'édition de liens, les dépendances et les différences entre plateformes. Lancer g++ *.cpp à la main ne passe pas à l'échelle : vous avez besoin de builds incrémentaux (ne recompiler que les fichiers modifiés), de la détection des bibliothèques, de la prise en charge multiplateforme et de builds reproductibles. CMake est devenu le générateur de système de build standard de fait pour le C++. Il ne compile pas le code lui-même — il génère les fichiers de build natifs (Makefiles, fichiers Ninja, projets Visual Studio) pour votre plateforme.
Les bases de CMake
Un fichier CMakeLists.txt décrit la structure de votre projet. Le CMake moderne (3.12+) adopte une approche orientée cibles dans laquelle tout est rattaché à des cibles (exécutables ou bibliothèques) :
# CMakeLists.txt — minimum viable project
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
# Set C++ standard for the entire project
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # use -std=c++20, not -std=gnu++20
# Create an executable target
add_executable(myapp
src/main.cpp
src/engine.cpp
src/utils.cpp
)
# Add include directories for this target
target_include_directories(myapp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Create a library target
add_library(mathlib STATIC
src/math/vector.cpp
src/math/matrix.cpp
)
target_include_directories(mathlib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include/math
)
# Link the library to the executable
target_link_libraries(myapp PRIVATE mathlib)
# Compiler warnings — essential for quality code
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic -Werror>
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)Compiler avec CMake
CMake fonctionne en deux étapes : la configuration (génération des fichiers de build) et le build (compilation). Compilez toujours hors de l'arborescence des sources (out-of-source) pour la garder propre :
# Configure — generate build files in a 'build' directory
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build — compile the project
cmake --build build --parallel $(nproc)
# Install (optional)
cmake --install build --prefix /usr/local
# Common build types:
# Debug — -O0 -g (full debug symbols, no optimization)
# Release — -O3 -DNDEBUG (max optimization, no asserts)
# RelWithDebInfo — -O2 -g -DNDEBUG (optimized with debug info)
# MinSizeRel — -Os -DNDEBUG (optimize for size)
# Run with sanitizers (add to CMakeLists.txt or command line)
cmake -B build-asan -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"FetchContent : la gestion des dépendances
Le module FetchContent de CMake télécharge et compile les dépendances au moment de la configuration, ce qui permet d'intégrer facilement des bibliothèques sans installation manuelle :
cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES CXX)
include(FetchContent)
# Fetch GoogleTest
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# Fetch fmt (formatting library)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.2.1
)
# Download and make available
FetchContent_MakeAvailable(googletest fmt)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)
# Test executable
enable_testing()
add_executable(tests tests/test_main.cpp)
target_link_libraries(tests PRIVATE GTest::gtest_main)
add_test(NAME unit_tests COMMAND tests)vcpkg et Conan
Pour les projets plus importants comportant de nombreuses dépendances, les gestionnaires de paquets dédiés au C++ offrent une meilleure résolution des dépendances et une mise en cache :
vcpkg (Microsoft) — s'intègre directement à CMake via un fichier de toolchain. Installez les paquets globalement ou en « mode manifeste » avec un fichier vcpkg.json :
{
"dependencies": ["fmt", "spdlog", "nlohmann-json", "catch2"]
}Utilisation : cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
Conan — utilise un conanfile.txt ou un conanfile.py pour déclarer les dépendances. Il prend en charge plusieurs configurations de build et la mise en cache des binaires :
[requires]
fmt/10.2.1
spdlog/1.13.0
[generators]
CMakeDeps
CMakeToolchainCes deux gestionnaires de paquets maintiennent des dépôts de milliers de bibliothèques C++, avec des binaires testés et précompilés.
Suivez ces règles pour un CMake maintenable :
1. Utilisez toujours les commandes orientées cibles : target_include_directories, target_compile_options, target_link_libraries — jamais include_directories ni add_definitions (qui polluent la portée globale).
2. Comprenez PRIVATE/PUBLIC/INTERFACE : PRIVATE = uniquement pour cette cible, PUBLIC = pour cette cible et ses consommateurs, INTERFACE = uniquement pour les consommateurs.
3. Définissez le standard C++ par cible ou pour tout le projet, jamais via des options brutes comme -std=c++20.
4. Activez un maximum d'avertissements : -Wall -Wextra -Wpedantic (GCC/Clang) ou /W4 (MSVC). Envisagez -Werror / /WX en CI.
5. Utilisez cmake --preset pour des builds reproductibles (CMakePresets.json).
6. Ne codez jamais de chemins en dur — utilisez find_package, FetchContent ou des gestionnaires de paquets.
Utiliser file(GLOB ...) pour collecter les sources : file(GLOB SOURCES src/*.cpp) n'est PAS réexécuté lorsque des fichiers sont ajoutés ou supprimés. Les nouveaux fichiers ne seront pas compilés tant que vous n'aurez pas relancé la configuration manuellement. Listez toujours les fichiers sources explicitement.
Oublier CMAKE_CXX_EXTENSIONS OFF : sans cela, GCC utilise -std=gnu++20 au lieu de -std=c++20, ce qui active des extensions non standard qui nuisent à la portabilité.
Mélanger add_subdirectory et FetchContent : les deux intègrent des projets externes, mais ils interagissent différemment avec le cache de build. Choisissez une seule approche par dépendance.
Ne pas définir CMAKE_EXPORT_COMPILE_COMMANDS ON : cette option génère compile_commands.json, nécessaire à clang-tidy, clangd (LSP) et à de nombreuses intégrations d'IDE. Activez-la toujours.
- CMake est le générateur de système de build standard du C++ — apprenez son API orientée cibles
- Utilisez les commandes
target_*(et non la portée globale) avec la visibilité PRIVATE/PUBLIC/INTERFACE - FetchContent télécharge et compile les dépendances au moment de la configuration — idéal pour un petit nombre de dépendances
- vcpkg (en mode manifeste) et Conan gèrent des graphes de dépendances plus vastes avec mise en cache des binaires
- Activez toujours les avertissements (
-Wall -Wextra -Wpedantic) et compilez hors de l'arborescence des sources - Générez
compile_commands.jsonpour les IDE et les outils d'analyse statique
Quiz — Testez vos connaissances
(15 XP)1. Que fait `target_link_libraries(myapp PRIVATE mathlib)` ?
2. Pourquoi `file(GLOB SOURCES src/*.cpp)` est-il considéré comme une mauvaise pratique avec CMake ?
3. À quoi sert `CMAKE_CXX_EXTENSIONS OFF` ?