Introduction à CMake

Table des matières

1 Introduction

Ce cours a pour vocation d'introduire à l'outil CMake en s'appuyant sur l'expérience acquise par Michel Simatic dans l'utilisation de CMake pour le cours CSC4526 de Télécom SudParis. Ce cours n'est donc qu'une introduction forcément partielle, mais qui permet quand même un bon survol des possibilités de CMake.

2 Qu'est-ce que CMake ?

  • CMake est un méta-"système de construction" destiné à générer, pour la plupart des systèmes d'exploitation existants (Windows, Linux, MacOS, etc.), des fichiers de configuration pour des "systèmes de construction" de logiciel: make, MSBuild, Ninja
  • Il facilite la vie des développeurs·ses en :
    • limitant les besoins d'installation de bibliothèques avant de pouvoir compiler le logiciel
    • leur évitant de se préoccuper des soucis de configuration d'outil de construction de logiciel, compilateur et éditeur de liens.
      • en particulier, facilitant le développement d'une version debug ou production.
    • permettant de déclencher, sur un simple commit Git, la compilation du logiciel avec différents compilateurs (MSVC, gcc, clang) avec, par exemple, Microsoft Azure Pipelines.

3 TP montrant l'utilisation de CMake

Décompressez l'archive SampleSFML.zip dans le répertoire de votre choix (par exemple, dans C:\users\votre_login\CSC4526).

3.1 Sous Linux, CMake en mode commande (utilisation de l'outil de construction make)

  • Sous Linux, ouvrez un terminal et tapez les commandes :
cd cheminVersLeRepertoireSampleSFML
mkdir buildUnix
cd buildUnix
cmake ..
make
sample/sample # Sous Linux (bin/sample sous MacOS)
  • Observez que :
    • Le répertoire _deps contient le résultat du ~ git clone~ de SFML.
    • Tout le travail de construction du logiciel a été fait dans ce répertoire buildUnix. Il suffit d'effacer ce répertoire pour s'en débarrasser.

3.2 Sous Linux, CMake avec CLion (utilisation de l'outil de construction Ninja)

  • Sous Linux, exploitez le canevas de projet obtenu selon la procédure cmake de ce document.
  • Observez que :
    • Le travail de Clion n'est pas dérangé par la présence du répertoire buildUnix.
    • Tout le travail de construction du logiciel a été réalisé dans le répertoire cmake-build-debug.
    • CLion a également créé un répertoire .idea qui contient les données de travail de CLion sur ce projet (par exemple, l'indexation des fichiers).
    • Si besoin de "réinitialiser" l'environnement, il suffit d'effacer ces deux répertoires cmake-build-debug et .idea.

3.3 Sous Windows, CMake en mode commande (utilisation de l'outil de construction MSBuild)

  • Sous Windows, lancez VisualStudio et "Outils > Ligne de commande > Powershell développpeur".
  • Dans ce terminal, tapez les commandes :
cd cheminVersLeRepertoireSampleSFML
mkdir buildWindows
cd buildWindows
# Si on a lancé la fenêtre de commande en dehors de Visual Studio, il faut exécuter 
# la commande suivante pour correctement initialiser la variable d'environnement Path (pour voir 
# le contenu de cette variable, tapez la commande : echo $env:path ) et donc accéder à tous les 
# outils VisualStudio) :
# C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat
cmake ..
MSBuild.exe .\JIN4_SampleSFML.sln
.\sample\Debug\sample.exe
  • Observez que :
    • Le répertoire _deps contient le résultat du ~ git clone~ de SFML.
    • Tout le travail de construction du logiciel a été fait dans ce répertoire buildWindows. Il suffit d'effacer ce répertoire pour s'en débarrasser.

3.4 Sous Windows, CMake avec VisualStudio (utilisation de l'outil de construction Ninja)

  • Sous Windows, exploitez le canevas de projet obtenu selon la procédure cmake de ce document.
  • Observez que :
    • Le travail de VisualStudio n'est pas dérangé par la présence du répertoire buildUnix.
    • Tout le travail de construction du logiciel a été réalisé dans le répertoire .out.
    • VisualStudio a également créé un répertoire .vs qui contient les données de travail de CLion sur ce projet (par exemple, l'indexation des fichiers).
    • Si besoin de "réinitialiser" l'environnement, il suffit d'effacer ces deux répertoires out et .vs.

4 Configuration de CMake : les fichiers CMakeLists.txt

  • Pour un projet logiciel donné, on aura au moins 2 fichiers CMakeLists.txt à écrire. Nous les détaillons ci-après.

4.1 CMakeLists.txt global

  • Ce fichier (présent dans le répertoire racine du projet) contient les informations communes à toutes les entités logicielles (exécutable ou bibliothèque) de votre projet.
  • Affichez SampleSFML/CMakeLists.txt

4.1.1 cmake_minimum_required(VERSION 3.15)

Spécifie que vous avez au moins besoin de CMake 3.15 pour votre projet. NB : Dans CSC4526, on a besoin d'avoir au moins la version 3.15 de CMake pour disposer du module CMake FetchContent.

4.1.2 cmake_policy(VERSION 3.15)

Désormais inutile, car, à partir de CMake version 2.4, "The cmake_minimum_required(VERSION) command implicitly invokes the cmake_policy(VERSION) command" (cf. https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html)

4.1.3 include(FetchContent)

Spécifie qu'on utilise le module CMake FetchContent.

4.1.4 project(JIN4_SampleSFML VERSION 1.0.0 LANGUAGES CXX)

Spécifie le nom de votre projet dans l'outil de construction (cf. JIN4_SampleSFML.sln), sa version et les langages informatiques utilisés pour votre projet.

4.1.5 IF(WIN32), else() et endif()

Permettent une génération conditionnelle

4.1.6 set (CMAKE_EXPORT_COMPILE_COMMANDS ON)

Permet de positionner un flag de génération CMake dont SonarLint a besoin, sous Windows, pour travailler correctement après.

4.1.7 set (BUILD_SHARED_LIBS FALSE)

Indique à CMake de générer des bibliothèques statiques (et non dynamiques). Ainsi, même si les exécutables résultant sont plus gros, il n'y a rien à configurer au niveau du système d'exploitation des développeurs·ses pour que l'exécution puisse se faire correctement.

4.1.8 find_package

Demande à CMake de vérifier que ces bibliothéques sont bien installées sur la machine.

4.1.9 include_directories

Pour les futures compilation, configure le compilateur pour qu'il référence les répertoires en paramètre en tant que répertoires d'include.

4.1.10 FetchContent_Declare

Spécifie comment récupérer une bibliothèque externe (son repository GIT, la branche, le tag de version à utiliser).

4.1.11 FetchContent_MakeAvailable

Va effectivement récupérer une bibliothèque externe.

4.1.12 set(CMAKE_CXX_STANDARD 17)

Spécifie que CMake doit dire au compilateur d'autoriser les spécificités du C++17 (ce qui inclut C++11, C++14, mais pas C++20).

4.1.13 add_subdirectory(sample)

Ajoute le sous-répertoire sample à la construction.

4.1.14 enable_testing()

Permet de spécifier qu'il y aura des fichiers pour les tests dans le répertoire courant et ses sous-répertoires. NB : Cette instruction doit absolument apparaître dans le CMakeLists.txt de plus haut niveau pour permettre à l'explorateur de tests de Visual Studio de trouver les tests unitaires.

4.2 CMakeLists.txt par entité logicielle

  • Ce fichier (présent dans le répertoire contenant les sources de votre entité logicielle) contient les informations permettant de dire comment construire votre entité logicielle.
  • Affichez SampleSFML/sample/CMakeLists.txt

4.2.1 file(GLOB SOURCES CONFIGURE_DEPENDS *.h *.cpp)

Spécifie une variable CMake qui contient la liste de tous les fichiers .h et .cpp de votre répertoire courant.

4.2.2 add_executable

Spécifie que votre entité logiciell générée est un exécutable du nom de sample construit avec tous les fichiers issus de la liste de fichiers dans la variable CMake SOURCES.

4.2.3 target_link_libraries(sample PUBLIC sfml-graphics)

Spécifie que, pour construire votre entité logicielle sample, vous aurez besoin de faire l'édition de liens avec la bibliothèque sfml-graphics (et donc, implicitement, que vous aurez besoin des include SFML pour la compilation de vos .cpp

5 Exemples de projets avec CMake

  • SampleGoogleTest.zip est un exemple de projet contenant la génération d'une bibliothèque et de 2 exécutables s'appuyant sur cette bibliothèque.
    • NB : L'exécutable des tests unitaires n'est pas généré dans un répertoire dédié aux tests unitaires, à cause d'une limitation de l'explorateur de tests Visual Studio (qui ne voit pas les tests unitaires s'ils sont dans un sous-répertoire).
  • ProgrammationConcurrente.zip est un exemple de projet contenant la génération de 26 exécutables.

6 Quelques trucs et astuces

  • Certaines entités logicielles (par exemple, sqlite) sont écrites complètement en C. Dans ce cas, il faut expressément dire à CMake qu'il doit utiliser l'éditeur de lien C++ et pas l'éditeur de lien C :
add_library(sqlite sqlite3.c)
set_target_properties(sqlite PROPERTIES LINKER_LANGUAGE CXX)
  • Parfois, il y a besoin d'ajouter une option pour le linker (par exemple, l'option -pthread pour obtenir un exécutable Unix multithreadé):
add_executable(02_CounterMultithread
  Source_02.cpp
  )
if (UNIX)
  target_link_options(02_CounterMultithread PUBLIC -pthread)
endif()
  • Pour mettre le niveau maximum de warnings à la compilation:
if (MSVC)
    # warning level 4 (see https://docs.microsoft.com/fr-fr/cpp/build/reference/compiler-option-warning-level?view=vs-2019)
    # We do not put /WX to consider all warnings as errors
    add_compile_options(/W4)
else()
    # lots of warnings 
    # We do not put -Werror to consider all warnings as errors
    add_compile_options(-g -Wall -Wextra)
endif()
  • Recopie d'un dossier Media pour qu'il soit vu par l'exécutable généré:
add_custom_target(copy-Media ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Media)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Media
		   DEPENDS ${CMAKE_SOURCE_DIR}/Media
		   COMMAND ${CMAKE_COMMAND} -E copy_directory
			   ${CMAKE_SOURCE_DIR}/Media
			   ${CMAKE_CURRENT_BINARY_DIR}/Media
			   )
add_dependencies(my_executable copy-Media)
  • S'appuyer sur des bibliothèques installées manuellement sur la machine
set (PHOTON_DIR "C:/Software/Photon-Windows-Sdk_v4-1-16-3")
if(MSVC)
  find_library(PHOTON_COMMON Common-cpp_vc16_debug_windows_md_x64.lib PATHS ${PHOTON_DIR}/Common-cpp/lib REQUIRED)
  find_library(PHOTON_PHOTON Photon-cpp_vc16_debug_windows_md_x64.lib PATHS ${PHOTON_DIR}/Photon-cpp/lib REQUIRED)
  find_library(PHOTON_LOADBALANCING LoadBalancing-cpp_vc16_debug_windows_md_x64.lib PATHS ${PHOTON_DIR}/LoadBalancing-
cpp/lib REQUIRED)
elseif(APPLE)
  find_library(PHOTON_COMMON libCommon-cpp_debug_macosx.a PATHS ${PHOTON_DIR}/Common-cpp/lib REQUIRED)
  find_library(PHOTON_PHOTON libPhoton-cpp_debug_macosx.a PATHS ${PHOTON_DIR}/Photon-cpp/lib REQUIRED)
  find_library(PHOTON_LOADBALANCING libLoadBalancing-cpp_debug_macosx.a PATHS ${PHOTON_DIR}/LoadBalancing-cpp/lib REQU
IRED)
else()
  # Linux
  find_library(PHOTON_COMMON libCommonDebug64.a PATHS ${PHOTON_DIR}/Common-cpp REQUIRED)
  find_library(PHOTON_PHOTON libPhotonDebug64.a PATHS ${PHOTON_DIR}/Photon-cpp REQUIRED)
  find_library(PHOTON_LOADBALANCING libLoadBalancingDebug64.a PATHS ${PHOTON_DIR}/LoadBalancing-cpp REQUIRED)
endif()
target_link_libraries(Multi_Photon_V0 PUBLIC sfml-graphics ${PHOTON_LOADBALANCING} ${PHOTON_PHOTON} ${PHOTON_COMMON} )

Date: 31 mai 2022

Auteur: Michel Simatic

Created: 2023-04-27 Thu 18:19

Validate