Table des matières
cmake
Compilation
Pré-requis sous Linux
sudo apt-get install libssl-dev
.
Compilation
git clone https://gitlab.kitware.com/cmake/cmake cd cmake cmake -B build -DCMAKE_USE_OPENSSL:BOOL=ON -DBUILD_TESTING:BOOL=OFF -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel 16 sudo 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
# Etape de configuration. CXXFLAGS="..." cmake -S . -B build -DCMAKE_BUILD_TYPE="Release" # Etape de compilation. 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 la variable d'environnement 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
- Effacer le cache et regénérer les CMakeLists.txt
Il faut effacer le fichier CMakeCache.txt
et le dossier CMakeFiles
dans le dossier de compilation. Cela permet de conserver les binaires / fichiers objets déjà compilés.
- Différences entre les générateurs
Certaines générateurs (Unix Makefiles
et Ninja
) ne supportent qu'un seul type de compilation à la fois. Il faut définir le type de compilation avec CMAKE_BUILD_TYPE
.
D'autres générateurs (Visual Studio
, Xcode
, Ninja Multi-Config
) acceptent plusieurs types de compilation. Il faut passer par CMAKE_CONFIGURATION_TYPES
qui vaut par défaut Debug;Release;MinSizeRel;RelWithDebInfo
.
CMAKE_BUILD_TYPE
est indispensable lors de la génération des CMakeLists.txt pour les générateurs mono-build.
--config
est indispensable lors de la compilation pour les générateurs multi-build.
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
Voir une version à jour avec tous les commentaires dans cmake-library.
add_library
- Depuis le code source
add_library(Librairie sources.cpp sources.hpp CMakeLists.txt) add_library(NameSpace::Librairie ALIAS Librairie) set(Librairie_SRC ${CMAKE_CURRENT_SOURCE_DIR}/sources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) set(Librairie_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/sources.h) target_sources( Librairie PRIVATE ${Librairie_SRC} PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ FILES ${Librairie_HEADER})
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 une librairie dynamique précompilée
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
Suivre les guides :
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_AS=/usr/bin/llvm-as -DCMAKE_RANLIB=/usr/bin/llvm-ranlib -DCMAKE_LINKER_TYPE=LLD
DWARF error: invalid or unhandled FORM value: 0x23
Ne pas spécifier CMAKE_LINKER_TYPE
va entrainer cette erreur. C++17 leads to linker error with ld due to some strange debug info? Archive du 08/08/2022 le 31/05/2024
Sinon, il est aussi possible de mettre l'option de compilation -gdwarf-4
pour les flags C
et CXX
.
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}
Environnement de compilation et de sortie
Environnement de sortie
if(WIN32) set(OS "Windows") elseif(UNIX AND NOT APPLE) set(OS "Linux/Unix") elseif(APPLE) set(OS "macOS") else() set(OS "Unknown") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(ARCHITECTURE "64-bit") elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(ARCHITECTURE "32-bit") else() set(ARCHITECTURE "Unknown") endif()
Exécution d'une commande
En passant par une nouvelle target
add_custom_target(clang_fmt ALL COMMAND xxxxxx)
Associé une commande à une target existante
- En passant par une target intermédiaire
add_custom_target(clang_fmt_dep COMMAND ...) add_dependencies(clang_fmt clang_fmt_dep)
- Sans passer par une target intermédiaire
add_custom_command(TARGET clang_fmt PRE_BUILD COMMAND ...)
- En passant par un fichier qui sera généré
add_custom_command(OUTPUT file COMMAND ...) add_dependencies(clang_fmt file)
Spécificité au générateur Visual Studio
Normalement, une commande échoue si elle ne renvoie pas 0.
Mais Visual Studio va également analyser la sortie standard. Si le contenu de la sortie standard est faussement considéré comme une erreur, il faut rediriger la sortie standard vers NUL.
if(CMAKE_GENERATOR MATCHES "Visual Studio") set(IGNORE_ERROR_IN_LOG ">" "NUL" "2>&1") else() set(IGNORE_ERROR_IN_LOG "") endif() add_custom_target(targ COMMAND ... ${IGNORE_ERROR_IN_LOG}) )
Ce problème peut se produire en lançant la commande `python -m ensurepip` car elle lance la sous-commande `pip install` et cette commande peut générer l'erreur :
"...\target.vcxproj" (default target) (5) -> (CustomBuild target) -> CUSTOMBUILD : error : pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. [...\target.vcxproj] C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets(254,5): error MSB8066: Custom build for '...\file.rule;...\target.rule;...\CMakeLists.txt' exited with code -1. [...\target.vcxproj] 15 Warning(s) 2 Error(s)
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
Rustines à appliquer systématiquement
configure_file
Il faut forcer la configuration si le fichier a été supprimé depuis la configuration initiale de CMake.
configure_file(version.h.in version.h) # Need to regenerate version.h if you remove it. # https://gitlab.kitware.com/cmake/cmake/-/issues/18985 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/version.h")
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})
Divers
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
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, ligne par ligne, d'un run
CMake
(configuration uniquement)
Ajouter l'option --trace-expand
.
- Afficher le temps d'exécution de chaque ligne CMake (configuration uniquement)
Ajouter les options --profiling-format=google-trace --profiling-output=perf.json
et lire via Perfetto.
- Activer l'affichage des lignes de compilation
Ajouter dans le CMakeLists.txt
: set(CMAKE_VERBOSE_MAKEFILE ON)
. Ou lancer VERBOSE=1 cmake …
- Afficher le temps pour chaque compilation pour chaque objet
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CMAKE_COMMAND} -E time")
Ne fonctionne que pour les Makefile et Ninja. Pas avec Visual Studio.
Inconvénient : il faut tout compiler sans l'option -j
. Sinon, tous les temps affichés sont mélangés.