Exercice pratique CMake
Cet article montre comment utiliser CMake pour gérer un projet C++ modérément complexe, couvrant le processus complet de la création du projet à la compilation et à l'exécution.
Le contenu couvre la configuration de base, la liaison de bibliothèques, les tests unitaires, les commandes personnalisées et des fonctionnalités avancées telles que la compilation croisée multiplateforme.
Processus de build CMake
Le diagramme suivant montre le processus complet de build CMake, du code source à l'exécutable final :
Aperçu du projet
Nous allons construire un projet C++ qui contient un programme principal, une bibliothèque statique et des tests unitaires.
La bibliothèque fournit des opérations mathématiques (addition, soustraction, multiplication). Le programme principal appelle ces fonctions, et le code de test vérifie leur exactitude.
Structure des répertoires du projet
MyProject/
├── CMakeLists.txt # 根构建配置(项目定义、全局设置)
├── src/
│ ├── main.cpp # 主程序入口
│ ├── CMakeLists.txt # src 子目录构建配置(库 + 可执行文件)
│ ├── lib/
│ │ ├── module1.cpp # 数学模块一:加法、减法
│ │ └── module2.cpp # 数学模块二:乘法
│ └── include/
│ └── mylib.h # 公共头文件(函数声明)
└── tests/
├── test_main.cpp # 单元测试用例
└── CMakeLists.txt # 测试子目录构建配置
Compilation hors sourceC'est la bonne pratique de CMake : tous les artefacts de build (Makefiles, fichiers objets, exécutables) sont placés dans un répertoire de build séparé, ce qui garde le répertoire source propre.
Création du fichier CMakeLists.txt
CMakeLists.txt est le fichier de configuration central de CMake. Il utilise une structure hiérarchique, avec un fichier dans le répertoire racine et un dans chaque sous-répertoire.
CMakeLists.txt du répertoire racine
La configuration racine définit les informations au niveau du projet et gère les sous-répertoires via add_subdirectory.
Exemple
project(MyProject VERSION 1.0) # Définir le nom et le numéro de version du projet
# Définir la norme C++ sur C++11 et la rendre obligatoire
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Ajouter un chemin de recherche global pour les fichiers d'en-tête (PROJECT_SOURCE_DIR est le répertoire racine du projet)
include_directories(${PROJECT_SOURCE_DIR}/src/include)
# Ajouter des sous-répertoires ; CMake traitera leurs fichiers CMakeLists.txt dans l'ordre
add_subdirectory(src) # Traiter la bibliothèque et l'exécutable
add_subdirectory(tests) # Traiter les tests
CMakeLists.txt du répertoire src
Ce fichier définit la cible de bibliothèque (MyLib) et la cible exécutable (MyExecutable), et établit la relation de dépendance.
Exemple
add_library(MyLib STATIC
lib/module1.cpp
lib/module2.cpp
)
# Spécifier le chemin d'accès public des fichiers d'en-tête pour MyLib (PUBLIC signifie que les consommateurs hériteront aussi de ce chemin)
target_include_directories(MyLib PUBLIC ${CMAKE_SOURCE_DIR}/src/include)
# Créer la cible exécutable MyExecutable
add_executable(MyExecutable main.cpp)
# Lier MyLib à l'exécutable (PRIVATE signifie que seul MyExecutable en a besoin)
target_link_libraries(MyExecutable PRIVATE MyLib)
La différence entre PRIVATE et PUBLIC :PRIVATEIndique que la dépendance n'est requise que par la cible actuelle,PUBLICIndique que la dépendance est propagée aux autres cibles qui sont liées à la cible actuelle.
Répertoire tests CMakeLists.txt
La configuration des tests utilise le framework Google Test ; commencez par rechercher et inclure le paquet GTest.
Exemple
# Afficher un message après la construction de MyExecutable
add_custom_command(
TARGET MyExecutable
POST_BUILD # Exécuter après la compilation
COMMAND ${CMAKE_COMMAND} -E echo "Compilation de MyExecutable terminée !"
COMMENT "Afficher le message de fin de compilation" # Commentaire affiché dans le journal de compilation
)
Cibles personnalisées (add_custom_target)
Une cible personnalisée est une cible indépendante qui ne produit pas de fichiers de sortie et sert à effectuer des tâches spécifiques.
Exemple
# Créez une cible run pour exécuter le programme compilé
add_custom_target(run
COMMAND ${CMAKE_BINARY_DIR}/src/MyExecutable
DEPENDS MyExecutable # Dépend de MyExecutable pour s'assurer qu'il est compilé en premier
COMMENT "Exécuter MyExecutable"
)
Exécution d'une cible personnalisée :
# 在 build 目录下执行 make run # ou cmake --build . --target run
Différences entre les commandes personnalisées et les cibles personnalisées :add_custom_commandAttachée à une cible, déclenchée à une étape spécifique de la construction de la cible ;add_custom_targetEst une cible indépendante, exécutée uniquement lorsqu'elle est explicitement spécifiée.
Multiplateforme et compilation croisée
L'un des avantages fondamentaux de CMake est la prise en charge multiplateforme, ce qui permet de changer facilement de plateforme et d'architecture cibles.
Spécification de la plateforme cible
La plateforme cible peut être spécifiée à l'aide de la variable CMAKE_SYSTEM_NAME.
# 为 Linux 平台交叉编译 cmake -DCMAKE_SYSTEM_NAME=Linux .. # 为 Windows 平台交叉编译 cmake -DCMAKE_SYSTEM_NAME=Windows ..
Utilisation des fichiers de chaîne d'outils
Pour les scénarios de compilation croisée complexes, il est recommandé d'utiliser un fichier de chaîne d'outils pour gérer de manière centralisée toutes les configurations liées à la plateforme.
Tout d'abord, créez le fichier de chaîne d'outils toolchain.cmake :
Exemple
# Configuration de la chaîne d'outils de compilation croisée ARM Linux
# Spécifier le système cible
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
# Spécifier le chemin du compilateur croisé (à adapter selon l'environnement réel)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# Spécifiez le chemin du système de fichiers racine du système cible (facultatif, utilisé pour trouver les bibliothèques et les fichiers d'en-tête)
set(CMAKE_FIND_ROOT_PATH /path/to/arm/sysroot)
# Rechercher uniquement les programmes dans le sysroot cible (et non dans le système hôte)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Rechercher uniquement les fichiers de bibliothèque dans le sysroot cible
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# Rechercher uniquement les fichiers d'en-tête dans le sysroot cible
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Construction avec le fichier de chaîne d'outils :
# 在 build 目录下执行 cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake .. cmake --build .
Le fichier de chaîne d'outils gère de manière centralisée les configurations liées à la plateforme, telles que les compilateurs, les éditeurs de liens et les chemins des bibliothèques, de sorte que changer de plateforme cible nécessite uniquement de spécifier un fichier de chaîne d'outils différent sans modifier CMakeLists.txt.
Types de build et options courantes
CMake prend en charge plusieurs types de build, contrôlés par la variable CMAKE_BUILD_TYPE.
| Type de build | Options du compilateur | Scénarios applicables |
|---|---|---|
| Debug | -g (symboles de débogage) | Phase de développement et de débogage, lorsque les points d'arrêt et l'inspection des variables sont nécessaires |
| Release | -O2 -DNDEBUG (optimisation + suppression des assertions) | Version de production, visant la vitesse d'exécution |
| RelWithDebInfo | -O2 -g (optimisation + symboles de débogage) | Version Release nécessitant une analyse des performances ou un débogage optimisé |
| MinSizeRel | -Os (optimisation de la taille) | Systèmes embarqués ou scénarios sensibles à la taille |
Spécification du type de compilation :
# Debug 构建 cmake -DCMAKE_BUILD_TYPE=Debug .. # Release 构建 cmake -DCMAKE_BUILD_TYPE=Release ..Autres extensions