Aller au contenu

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, utilisez NR_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.