Introduction à CMake

Michel Simatic

15 mai 2024

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 ?

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)

cd cheminVersLeRepertoireSampleSFML
mkdir buildUnix
cd buildUnix
cmake ..
cmake -- build . # Vous pouvez aussi taper la commande: make
src/sample # sous Linux (bin/sample sous MacOS)

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

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

cd cheminVersLeRepertoireSampleSFML
mkdir buildWindows
cd buildWindows
cmake ..
cmake --build . # Vous pouvez aussi taper la commande: MSBuild.exe .\JIN4_SampleSFML.sln
.\src\Debug\sample.exe
# NB : Si la fenêtre de commande a été lancée 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 amd64
# (NB : Ne fonctionne pas sous PowerShell ; en cours d'investigation)

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

4 Configuration de CMake : les fichiers CMakeLists.txt

4.1 CMakeLists.txt global

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

Cette commande spécifie comment récupérer une bibliothèque externe (son repository GIT et la branche/tag de version à utiliser ou bien l’URL où récupérer une archive).

cet article explique qu’il est beaucoup plus performant de récupérer un projet via l’URL d’une archive que via un dépôt GIT (qui fait un git clone). Dit autrement, préférez:

  FetchContent_Declare(
    sfml
    URL https://github.com/SFML/SFML/archive/refs/tags/2.6.1.tar.gz
  )

à

  FetchContent_Declare(
          sfml
          GIT_REPOSITORY https://github.com/SFML/SFML.git
          GIT_TAG 2.6.1
  )

Les tests que nous avons faits sur la bibliothèque cereal confirment cette opinion :

Seul bémol sur l’utilisation d’une URL par rapport à l’utilisation d’un dépôt GIT, le FetchContent_Declare doit être précédé par :

cmake_policy(SET CMP0135 NEW) # This cmake_policy avoids warning by cmake when we fetch contents based on URL

4.1.11 FetchContent_MakeAvailable

Cette commande récupère effectivement une bibliothèque externe.

4.1.12 set(CMAKE_CXX_STANDARD 20)

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

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 : Pour que l’explorateur de tests de Visual Studio travaille correctement, cette instruction doit absolument :

  1. apparaître dans le CMakeLists.txt de plus haut niveau (cf. documentation cmake),
  2. être précédée par l’instruction include(GoogleTest) dans le CMakeLists.txt de plus haut niveau (cf. ce ticket).

4.2 CMakeLists.txt par entité logicielle

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.

NB : Certain·e·s “pro” de cmake déconseillent d’utiliser cette instruction. Iels disent qu’il vaut mieux lister explicitement les fichiers.

4.2.2 add_executable

Spécifie que votre entité logicielle 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.

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

6 Quelques trucs et astuces

add_library(sqlite sqlite3.c)
set_target_properties(sqlite PROPERTIES LINKER_LANGUAGE CXX)
add_executable(02_CounterMultithread
  Source_02.cpp
  )
if (UNIX)
  target_link_options(02_CounterMultithread PUBLIC -pthread)
endif()
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()
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)
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} )

7 Pour aller plus loin

Ce tutoriel est très complet (il explique notamment comment construire un package d’installation avec l’instruction cpack).