Outils pour utilisateurs

Outils du site


prog:cmake

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
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.

CMake Archive du 25/04/2022 le 26/09/2022

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

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'un cmake par FetchContent_Declare.

The <contentOptions> can be any of the download, update or patch options that the ExternalProject_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é

  1. 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.

Ninja

  • Limiter le nombre de lieur en parallèle

Il est possible de limiter le nombre de link en parallèle. Le lieur est consommateur en ressource. Il peut être nécessaire de limiter leur nombre à une valeur plus basse que le nombre de compilateur.

set(CMAKE_JOB_POOLS "link=2")
set(CMAKE_JOB_POOL_LINK link)
prog/cmake.txt · Dernière modification : 2024/08/30 15:13 de root