Rendre une bibliothèque "consommable"
L'utilisation de la bibliothèque par un projet indépendant (sans que
le projet bibliothèque soit englobé dans l'autre projet) nécessite une
préparation. Il faut créer un fichier <packageName>Config.cmake
:
<packageName>
est le nom sous lequel la bibliothèque sera cherchée par le projet consommateur. En général, on choisit le nom du projet bibliothèque.- Le fichier contient les propriétés d'interface de la bibliothèque.
Création du fichier config, cas sans dépendances, sans installation
Dans le cas simple où la bibliothèque ne dépend pas elle-même d'autres
bibliothèques, et où on ne veut pas installer la bibliothèque (voir
plus loin pour la notion d'installation), le fichier peut être créé
par la commande suivante, à ajouter dans CMakeLists.txt
:
export(TARGETS <cible bibliothèque> FILE ${PROJECT_NAME}Config.cmake)
Création du fichier config, cas avec dépendances, sans installation
Si la bibliothèque A dépend elle-même d'autres bibliothèques, le
fichier AConfig.cmake
doit vérifier la disponibilité de ces
bibliothèques.
Ajouter la commande dans CMakeLists.txt
:
export(TARGETS A FILE ${PROJECT_NAME}Targets.cmake)
Noter la différence avec :
FILE ${PROJECT_NAME}Config.cmake
qui crée directement le fichier ${PROJECT_NAME}Config.cmake
dans le
cas sans dépendances. Ici, nous allons inclure
${PROJECT_NAME}Targets.cmake
dans ${PROJECT_NAME}Config.cmake
.
Créer manuellement un fichier AConfig.cmake.in
avec le contenu :
include(CMakeFindDependencyMacro)
find_dependency(B)
find_dependency(C)
...
include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake)
Ajouter dans CMakeLists.txt
la commande :
configure_file(${PROJECT_NAME}Config.cmake.in ${PROJECT_NAME}Config.cmake @ONLY)
qui copie le fichier dans le répertoire de compilation en remplaçant
les chaînes de caractères entre @
.
Utilisation d'une bibliothèque extérieure au projet
Ajouter dans CMakeLists.txt
du projet utilisateur :
find_package(<PackageName> CONFIG REQUIRED)
À l'exécution de cmake ou cmake-gui, si la bibliothèque utilisée n'est
pas trouvée automatiquement, donner le chemin du répertoire parent de
son répertoire de compilation avec la variable de cache
CMAKE_PREFIX_PATH
. Nota bene :
- Lui donner le type PATH.
-
CMAKE_PREFIX_PATH
est interprété comme une liste : vous pouvez donner plusieurs chemins (en général pour plusieurs bibliothèques) séparés par des points-virgules. -
Donner des chemins absolus.
Manipulation 25
- Compiler
NR_util
et Jumble avec deux projets indépendants. À la configuration de Jumble, utilisezNR_util
que vous venez de compiler et non la version installée dans~/.local
. - Préparer aussi la consommation de Jumble même si vous ne l'utilisez pas tout de suite.
Namespace
Conseil : ajouter une option NAMESPACE à la commande export :
export(TARGETS <cible> NAMESPACE <namespace> FILE ${PROJECT_NAME}....cmake)
L'effet est de préfixer le nom de la cible avec namespace dans
${PROJECT_NAME}....cmake
. Les projets consommateurs devront faire
référence à la cible préfixée avec namespace.
Terminer namespace par ::
. Typiquement, on choisit pour namespace :
${PROJECT_NAME}::
.
Namespace : exemple
Dans le CMakeLists.txt
de NR_util
:
export(TARGETS nr_util NAMESPACE ${PROJECT_NAME}:: FILE ${PROJECT_NAME}Config.cmake)
et dans le CMakeLists.txt
d'un projet consommateur de NR_util
:
find_package(NR_util CONFIG REQUIRED)
target_link_libraries(Jumble PRIVATE NR_util::nr_util)
Intérêts de namespace
Intérêt principal : si un nom contient ::
alors CMake le traite
comme le nom d'une cible importée (typiquement avec find_package
) ou
d'un alias (cf. plus loin). Si find_package
a été oublié ou s'il y a
une faute de frappe dans le nom, une erreur est signalée à la
génération. Si le nom ne contient pas ::
, CMake admet la possibilité
que ce soit le nom d'une bibliothèque à trouver dans les répertoires
du système, au moment de la compilation.
Exemple :
target_link_libraries(Jumble PRIVATE nr_util)
et on a oublié find_package
. CMake ajoute juste -lnr_util
dans les
options d'édition de liens et l'erreur n'apparaîtra qu'à la
compilation.
Autre exemple, on n'a pas oublié find_package
mais on a une faute
de frappe dans le nom de la cible consommée :
target_link_libraries(Jumble PRIVATE NR_util)
Là encore, l'erreur n'apparaîtra qu'à la compilation.
Intérêt secondaire de namespace : éviter une collision entre cibles consommées de noms identiques, venant de différents projets.
Laisser le choix entre englober le projet ou non : le problème de find_package
Si les projets A et B sont englobés alors la commande :
find_package(A CONFIG REQUIRED)
depuis B :
- d'une part est inutile, puisque la cible A est définie dans une partie du projet global ;
- d'autre part échoue (le fichier
AConfig.cmake
n'est pas encore créé au moment de la configuration).
Il faut d'une façon ou d'une autre tester l'opportunité de l'appel à
find_package
.
Première méthode :
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
find_package(A CONFIG REQUIRED)
endif()
Inconvénient : suppose que le projet englobant B englobe aussi A. Ou, en d'autres termes, que tout projet englobant B, englobera logiquement aussi A.
Seconde méthode :
if(NOT TARGET A)
find_package(A CONFIG REQUIRED)
endif()
Plus robuste. Le projet englobant B peut ne pas englober A.
Inconvénient : il faut faire attention à l'ordre des appels à
add_subdirectory
dans le projet englobant, add_subdirectory(A)
doit être avant add_subdirectory(B)
.
Laisser le choix entre englober le projet ou non : le problème du namespace
Supposons que A soit utilisé par B :
target_link(B ... Project_A::A)
et que A et B soient englobés. Le namespace Projet_A::
n'est ajouté
que dans le fichier AConfig.cmake
, à l'intention seule de
find_package
. Pour que la commande target_link
ci-dessus n'échoue
pas, il faut ajouter un alias dans le projet A :
add_library(PROJECT_A::A ALIAS A)
Manipulation 26
Modifier les CMakeLists.txt
de NR_util
, Jumble, Numer_Rec_95
et
Coriolis pour que ces codes puissent être compilés aussi bien
indépendamment qu'englobés dans un super-projet. Penser à mettre des
namespaces. Tester.