CMake : guide pratique de l'outil de génération de builds

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 :

  1. Rédaction du fichier CMakeLists.txt à la racine du projet.
  2. Configuration dans un répertoire de build séparé.
  3. Génération des fichiers du système de build natif.
  4. 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
)

Étiquettes: CMake C++ Ninja Makefile visual-studio

Publié le 12 juillet à 05h22