Ceci est une ancienne révision du document !
Table des matières
cmake
Compilation
git clone https://gitlab.kitware.com/cmake/cmake cd cmake cmake -B build cmake --build build cmake --install build
La méthode de bootstrap
n'est nécessaire que si on ne possède pas un cmake précompilé.
Build CMake from dev sources Archive du 01/03/2021 le 09/01/2023
Utilisation basique
- Générer et lancer la compilation
CXXFLAGS="..." cmake -S . -B build -DCMAKE_BUILD_TYPE="Release" cmake --build build/ --target all --parallel --config "Release"
La deuxième instruction n'a pas besoin d'autres arguments.
Ne pas utiliser cmake -DCMAKE_CXX_FLAGS="..."
mais plutôt CXXFLAGS="..." cmake
En compilant pour Visual Studio
, -DCMAKE_CXX_FLAGS
écrase la valeur qu'aurait dû générer cmake
(/DWIN32 /D_WINDOWS /W3 /GR /EHsc
par défaut).
En utilisant CXXFLAGS
, les flags sont ajoutés au début (... /DWIN32 /D_WINDOWS /W3 /GR /EHsc
).
Passing compiler options cmake Archive du 31/05/2017 le 05/10/2022
- Différences entre les générateurs
En fonction des générateurs (Unix Makefiles
pour Linux et Visual Studio 17 2022
pour Windows), il existe plusieurs types de build (CMAKE_BUILD_TYPE
) :
- Debug
- Release
- RelWithDebInfo,
- MinSizeRel
Debug
est évidemment Debug
et les 3 autres sont dans la catégorie Release
.
CMAKE_BUILD_TYPE
doit être passé dans le premier appel au cmake -S . -B build
. C'est indispensable pour Unix Makefiles
car le générateur ne supporte qu'un seul type de build à la fois. C'est facultatif pour Visual Studio XX YYYY
car il supporte plusieurs configurations à la fois. Cependant, il est quand même conseillé de l'utiliser à chaque fois pour la cohérence.
--config
doit être passé pour chaque cmake --build build
pour Visual Studio XX YYYY
mais est facultatif pour Unix Makefiles
. Il est aussi conseillé de toujours le spécifier.
Cross compilation pour Android
Il faut définir 3 variables d'environnement et passer quelques variables en lignes de commandes.
Pour Windows, il faut forcer l'utilisation de Ninja
car Visual Studio
n'est pas supporté.
export ANDROID_HOME="$HOME/AppData/Local/Android/Sdk" export ANDROID_SDK_PATH="$HOME/AppData/Local/Android/Sdk" export ANDROID_NDK_PATH="$HOME/AppData/Local/Android/Sdk/ndk/21.4.7075529" cmake -S dossier_source -B dossier_build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CONFIGURATION_TYPES=Release -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=30 -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DCMAKE_ANDROID_NDK="$HOME/AppData/Local/Android/Sdk/ndk/21.4.7075529" -DCMAKE_ANDROID_STL_TYPE:STRING="c++_static" -DCMAKE_INSTALL_PREFIX="install_prefix_path" -DBUILD_SHARED_LIBS:BOOL=OFF
La version de CMAKE_SYSTEM_VERSION
doit correspondre à minSdkVersion du fichier build.gradle
.
CMakeLists.txt
Cas courants
add_library
- Depuis le code source
add_library(Librairie sources.cpp sources.hpp CMakeLists.txt)
Le fichier CMakeLists.txt
sert uniquement à ajouter le fichier dans l'explorateur de Visual Studio.
target_link_libraries(Librairie PUBLIC Librairie2)
Ici, Librairie
dépend de Librairie2
.
PUBLIC
: Librairie2
est nécessaire pour compiler Librairie
et est également nécessaire pour qu'un exécutable puisse se lier à Librairie
.
PRIVATE
: Librairie2
est nécessaire pour compiler Librairie
mais n'est pas nécessaire pour qu'un exécutable puisse se lier à Librairie
.
INTERFACE
: Librairie2
n'est pas nécessaire pour compiler Librairie
mais est nécessaire pour qu'un exécutable puisse se lier à Librairie
. Cela est surtout nécessaire pour faire une librairie header-only.
- Depuis un binaire
add_library(Librairie SHARED IMPORTED GLOBAL) set_target_properties( Librairie PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION_DEBUG librairie-d.dll IMPORTED_LOCATION_RELEASE librairie.dll IMPORTED_IMPLIB_DEBUG librairie-d.lib IMPORTED_IMPLIB_RELEASE librairie.lib )
SHARED
ou STATIC
.
GLOBAL
: add_library
qui compile propage aux parents. add_library
qui importe ne propage pas aux parents.
IMPORTED_LOCATION
: emplacement de la librairie (.dll
/ .so
si SHARED
, .lib
/ .a
si STATIC
). Attention, la librairie dynamique ne doit pas avoir été renommée manuellement afin que le nom de fichier corresponde à son SONAME
.
IMPORTED_IMPLIB
: uniquement pour Windows et des librairies SHARED
. Le fichier .dll
ne contient pas les symboles. Il lui faut donc le .lib
associé. Attention, le fichier .lib
associé au .dll
(SHARED
) n'est pas le même que le .lib
compilé en STATIC
.
Librairie header-only
Follow guide:
Exemple sans find_package
: Creating a header only library with cmake Archive du 2017 le 11/07/2021
Exemple avec find_package
: Cmake line by line - creating a header-only library Archive du 24/06/2019 le 11/07/2021
Personnaliser la compilation
Il faut mettre les options après la déclaration de l'exécutable à compiler (add_executable
).
add_executable(boostexecutor executor.cpp main.cpp) target_include_directories(boostexecutor PRIVATE ${Boost_INCLUDE_DIRS}) target_compile_options(boostexecutor PRIVATE -O0) target_link_libraries(boostexecutor Threads::Threads)
Activation de l'optimisation global du lieur
Le LTCG (Link Time Code Generation) est identique à l'option lto (Link Time Optimization) de gcc.
Dans un projet Visual Studio, l'option est automatiquement mise pour le mode Release. Mais pas avec CMake.
Pour l'activer manuellement, il faut ajouter /GL
au compilateur et /LTCG
au lieur. Cela se fait via :
include(CheckIPOSupported) # définition du projet check_ipo_supported(RESULT result) if(result) set_property(TARGET hello PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) endif()
Cela active -lto
pour GCC et /LTCG:INCREMENTAL
pour Visual Studio.
Link Time Code Generation (LTCG) by default, for Release configurations of Visual Studio projects Archive du 05/02/2018 le 29/11/2019
Utiliser clang
Ajouter à cmake
:
-DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_AR=/usr/bin/llvm-ar -DCMAKE_RANLIB=/usr/bin/llvm-ranlib
FetchContent
- Il n'est pas possible d'ajout d'arguments de type
-Dxxx=ON
dans l'invocation d'uncmake
parFetchContent_Declare
.
The
<contentOptions>
can be any of the download, update or patch options that theExternalProject_Add()
command understands. The configure, build, install and test steps are explicitly disabled and therefore options related to them will be ignoredFetchContent Archive du v3.22 le 29/11/2021
Comme les paramètres CMAKE_ARGS
sont ignorés, il faut utiliser set
:
include(FetchContent) set(XXX YYY CACHE INTERNAL "") # Forces the value FetchContent_Declare(...
Passing CMake Arguments to FetchContent Archive du 23/03/2019 le 29/11/2021
Fonctions diverses
- Forcer
-fPIC
:set_property(TARGET 2lgcpoco PROPERTY POSITION_INDEPENDENT_CODE ON)
- Générer le fichier
compile_commands.json
:set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
Interaction avec la ligne de commande
Options
Les options sont les variables qui peuvent couramment être modifiées par l'utilisation mais qui doivent avoir une valeur par défaut.
Les options sont des variables CACHE
et ne peuvent donc être écrasés dans un fichier CMakLists.txt que par l'utilisation de CACHE ... FORCE
.
Deux déclarations équivalentes d'une variable CACHE
:
option(BUILD_STATIC "Build static libraries" ON) set(BUILD_STATIC OFF CACHE BOOL "Build static libraries")
Il est possible de modifier une variable CACHE
en utilisant set
avec l'option CACHE
.
option(BUILD_STATIC "Build static libraries" ON) set(BUILD_STATIC OFF CACHE BOOL "Build static libraries" FORCE)
Enfin la valeur finale d'une variable CACHE
est celle de l'utilisateur s'il la définit.
cmake . -DBUILD_STATIC:BOOL=OFF
Passer une liste de paramètres qui sera réutilisé comme une liste
Le séparateur est le ;
.
Bash:
cmake ... -DLISTE="-DVAR1=champ1;-DVAR2=champ2"
CMakeLists.txt, sans les guillemets.
${LISTE}
Cas spécifiques
Exécuter une action qui n'est rattachée à aucun programme/target
Il suffit de passer par une target
intermédiaire.
# On crée une target qui sera ajoutée à la commande make par défaut. add_custom_target(clang_fmt ALL) # On définir une commande à exécuter. add_custom_command(TARGET clang_fmt COMMAND xxxxxx)
Exécuter uniquement le préprocesseur
Il faut créer une target compilable en l'excluant de all
. Cela va créer des target intermédiaire, notamment xxx.cpp.i
. Il suffit alors de l'exécuter explicitement.
add_library(file_obj OBJECT EXCLUDE_FROM_ALL file.cpp) add_custom_target(file ALL COMMAND make file.cpp.i)
Generating preprocessed sources in CMake projects Archive du 21/11/2018 le 21/07/2020
Dépendances dans un sous-dossier
Il faut impérativement passer par une target
intermédiaire.
En un morceau :
add_custom_target (a_txt DEPENDS a.txt) add_custom_command (OUTPUT a.txt COMMAND cat {b,b}.txt > a.txt DEPENDS b.txt) add_custom_command (OUTPUT b.txt COMMAND paste {c,c}.txt > b.txt DEPENDS c.txt)
En deux morceaux :
add_subdirectory (sub) add_custom_target (a_txt DEPENDS a.txt) add_custom_command (OUTPUT a.txt COMMAND cat sub/{b,b}.txt > a.txt DEPENDS b_txt sub/b.txt)
add_custom_target (b_txt DEPENDS b.txt) add_custom_command (OUTPUT b.txt COMMAND paste {c,c}.txt > b.txt DEPENDS c.txt)
CMake dependencies on subdirectories Archive du 16/03/2012 le 29/11/2019
Déprécié
PUBLIC_HEADER
Utiliser à la place: FILE_SET HEADER
.
Ne pas utiliser car à l'installation, les fichiers entêtes sont installés sans conserver l'arborescence et le dossier de destination n'est pas respecté lors d'un :
install( TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}-${version})
Tests
Activer/Désactiver les tests
On commence par toujours inclure :
include(CTest)
Par défaut, BUILD_TESTING
est activé. Pour le désactiver, il faut ajouter -DBUILD_TESTING=OFF
lors de l'invocation de cmake
.
Puis, on peut mettre sous condition toutes les commandes concernant les tests :
if(BUILD_TESTING) add_test(...) ... endif(BUILD_TESTING)
Fichiers chargés par les tests
La compilation sous Windows supporte simultanément plusieurs build (Debug, Release, …). Donc, le dossier où se trouve les tests et le dossier de travail (working directory) n'est pas le même. L'un est (par exemple) racine/test/Debug
et l'autre est racine/test
.
Donc (à faire en connaissance de cause) pour copier un fichier du dossier source vers :
- le dossier de travail
add_custom_command( TARGET test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/../../file.txt" "${CMAKE_CURRENT_BINARY_DIR}")
- le dossier de l'exécutable
add_custom_command( TARGET test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/../../file.dll" $<TARGET_FILE_DIR:test>)
Librairie dynamique, tests et Windows
Souvent le dossier de compilation des tests n'est pas le même que celui des DLLs.
Il y a donc 2 solutions :
- Copier les DLLs dans le dossier des tests (recommandé)
A utiliser si le programme possède des DLL externes.
foreach(TESTI test_a_executer) add_test(NAME ${TESTI} COMMAND $<TARGET_FILE:${TESTI}>) if($<TARGET_RUNTIME_DLLS:${TESTI}>) add_custom_command( TARGET ${TESTI} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:${TESTI}> $<TARGET_FILE_DIR:${TESTI}> COMMAND_EXPAND_LISTS) endif() endforeach()
Dans le cas de Qt, il va manquer tout l'environnement Qt.
Solution Qt 1: copier les dlls comme précédemment et définir une variable d'environnement QT_QPA_PLATFORM_PLUGIN_PATH=C:\Qt\5.15.2\msvc2019_64\plugins\platforms
.
Solution Qt 2: ajouter tout l'environnement Qt. La définition de la variable VCINSTALLDIR
n'est nécessaire que pour Windows.
add_custom_command( TARGET target POST_BUILD COMMAND ${CMAKE_COMMAND} -E env VCINSTALLDIR=${CMAKE_GENERATOR_INSTANCE}/VC ${WINDEPLOYQT_EXECUTABLE} --qmldir ${CMAKE_CURRENT_SOURCE_DIR} --pdb "$<TARGET_FILE:target>")
- Compiler tous les binaires dans le même dossier
A utiliser si le programme possède uniquement des DLL internes gérées par le CMakeList.xtx.
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") add_test(NAME test_a_executer COMMAND $<TARGET_FILE:test_a_executer>)
CMake cannot find test if CMAKE_RUNTIME_OUTPUT_DIRECTORY is changed Archive du 28/01/2014 le 27/03/2023
RPATH
Eviter cette solution. Préférer celle du paragraphe Librairie dynamique, tests et Windows.
RPATH n'est applicable que sous Linux. Cette notion d'écrire les chemins vers les DLL dans l'exécutable n'existe pas sous Windows.
Les tests ne sont généralement pas installés et il faut que l'exécutable trouve le chemin vers les librairies.
Méthode officielle Archive du 04/03/2020 le 26/08/2022
Après avoir mis à jour CMAKE_INSTALL_RPATH
(set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${VTK_RUNTIME_DIRS}")
) ou l'attribut INSTALL_RPATH
via set_target_properties
, l'exécutable devrait trouver les librairies tout seul.
Si non, exécuter readelf -d executable
et vérifier que le champ RPATH
ou RUNPATH
est correctement rempli.
0x000000000000000f (RUNPATH) Library rpath: [path1:path2:path3]
Si les valeurs sont correctement remplies sous l'attribut RUNPATH
, il faut forcer l'utilisation de RPATH
. Ajouter au linker l'option -Wl,--disable-new-dtags
.
add_link_options("-Wl,--disable-new-dtags")
How to set RPATH and RUNPATH with GCC/LD? Archive du 25/08/2018 le 26/08/2022
Debug
- Afficher toutes les variables
CMake
Le fichier CMakeCache.txt ne stocke pas toutes les variables provenant des dépendances.
Il est possible de les afficher dans le terminal en ajoutant dans un fichier CMakeLists.txt
.
get_cmake_property(_variableNames VARIABLES) list (SORT _variableNames) foreach (_variableName ${_variableNames}) message(STATUS "${_variableName}=${${_variableName}}") endforeach()
CMake: Print out all accessible variables in a script Archive du 15/02/2012 le 18/08/2022
- Afficher toutes les étapes, line par line, d'un run
CMake
Ajouter l'option --trace-expand
.
- Activer l'affichage des lignes de compilation
Ajouter dans le CMakeLists.txt
: set(CMAKE_VERBOSE_MAKEFILE ON)
. Ou lancer VERBOSE=1 cmake …