Vue d'ensemble de CMake
CMake est un méta-générateur de systèmes de construction. Il ne compile pas directement le code source, mais produit les fichiers nécessaires à un outil de build natif. Selon la plate-forme cible, il génère des solutions Visual Studio (.sln), des Makefile, des projets Xcode ou des fichiers build.ninja. Cette abstraction permet d'écrire une seule fois la logique de construction et de la déployer sur Linux, macOS ou Windows.
Principaux avantages
- Portabilité : une description de projet unique pour plusieurs systèmes d'exploitation et compilateurs.
- Lisibilité : syntaxe plus concise et structurée que les Makefiles classiques.
- Détection automatique : commandes de recherche de bibliothèques, de programmes et de fonctionnalités système.
- Adoption large : utilisé par de nombreux projets C et C++.
Flux de travail
Un projet CMake suit habituellement ces étapes :
- Rédaction du fichier
CMakeLists.txtà la racine du projet. - Configuration dans un répertoire de build séparé.
- Génération des fichiers du système de build natif.
- Compilation avec l'outil correspondant (make, ninja, MSBuild...).
Installation
Ubuntu / Debian
sudo apt update
sudo apt install cmake
Pour obtenir la dernière version officielle via le dépôt Kitware :
sudo apt install apt-transport-https ca-certificates gnupg software-properties-common
wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | \
gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null
sudo apt-add-repository "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main"
sudo apt update
sudo apt install cmake cmake-qt-gui
cmake --version
CentOS / RHEL / Fedora
Sous CentOS et RHEL :
sudo yum install cmake3
sudo ln -s /usr/bin/cmake3 /usr/bin/cmake
Sous Fedora :
sudo dnf install cmake
macOS
Avec Homebrew :
brew install cmake
Pour l'interface graphique :
brew install cmake --with-qt
Windows
Le moyen le plus simple consiste à télécharger l'installateur sur le site officiel et à cocher l'option Add CMake to the system PATH. Avec Chocolatey :
choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System'
Compilation depuis les sources
wget https://github.com/Kitware/CMake/releases/download/v3.28.1/cmake-3.28.1.tar.gz
tar -xzf cmake-3.28.1.tar.gz
cd cmake-3.28.1
./bootstrap
make -j$(nproc)
sudo make install
cmake --version
Fondamentaux de la syntaxe
Le fichier central de tout projet est CMakeLists.txt. Il décrit le projet, ses cibles, ses dépendances et ses options de compilation.
Version minimale et définition du projet
cmake_minimum_required(VERSION 3.15)
project(DemoCalcul
VERSION 1.2.0
DESCRIPTION "Exemple de projet CMake"
LANGUAGES CXX
HOMEPAGE_URL "https://exemple.fr"
)
Variables
On utilise set pour déclarer des variables.
set(NOM_PROJET "DemoCalcul")
set(STANDARD_CXX 17)
set(SRCS_FICHIERS src/main.cpp src/matrice.cpp src/vecteur.cpp)
Création d'un exécutable
add_executable(${NOM_PROJET} ${SRCS_FICHIERS})
set_target_properties(${NOM_PROJET} PROPERTIES
CXX_STANDARD ${STANDARD_CXX}
CXX_STANDARD_REQUIRED ON
)
Création d'une bibliothèque
add_library(core STATIC src/matrice.cpp src/vecteur.cpp)
add_library(io SHARED src/fichier.cpp)
target_link_libraries(${NOM_PROJET} PRIVATE core io)
Chemins d'inclusion
Il est préférable d'associer les répertoires d'en-têtes à une cible précise plutôt qu'à l'ensemble du projet.
target_include_directories(core PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
PUBLIC indique que les dépendantes de core hériteront de ce chemin. PRIVATE le restreint à la cible elle-même.
Instructions conditionnelles
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_compile_definitions(${NOM_PROJET} PRIVATE SYSTEME_LINUX)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(${NOM_PROJET} PRIVATE SYSTEME_WINDOWS)
else()
target_compile_definitions(${NOM_PROJET} PRIVATE SYSTEME_AUTRE)
endif()
Messages et diagnostic
message(STATUS "Générateur choisi : ${CMAKE_GENERATOR}")
message(WARNING "Option expérimentale activée.")
message(FATAL_ERROR "Impossible de continuer : dépendance manquante.")
Recherche de paquets et de bibliothèques
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem)
find_library(LIB_PNG NAMES png libpng)
find_path(INC_ZLIB zlib.h)
find_program(EXEC_VALGRIND valgrind)
if(Boost_FOUND)
target_link_libraries(${NOM_PROJET} PRIVATE Boost::filesystem)
endif()
Utilisation de pkg-config
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET libavcodec libavformat)
target_link_libraries(${NOM_PROJET} PRIVATE PkgConfig::LIBAV)
Différence entre include_directories et target_include_directories
include_directories ajoute un répertoire d'en-têtes à toutes les cibles du répertoire courant. Cette approche est globale et peut propager des chemins non désirés.
include_directories(include)
target_include_directories applique le chemin à une cible spécifique avec une visibilité contrôlée (PRIVATE, PUBLIC ou INTERFACE). C'est la méthode recommandée pour les projets modernes.
target_include_directories(core PUBLIC include)
Installation des artefacts
install(TARGETS ${NOM_PROJET}
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)