Installation

(Cette partie ne traite bien sûr pas de l'installation de CMake lui-même, mais de l'installation d'un code que vous compilez avec CMake.)

L'idée d'installation

Installation : après la compilation, copie des exécutables ou des bibliothèques compilées vers une autre arborescence, avec d'autres exécutables ou bibliothèques venant de projets indépendants. Nous avons donc 3 arborescences pour un projet : source, compilation et installation. Intérêts de l'installation :

  • Regrouper pour mieux trouver. Le répertoire d'installation des exécutables peut faire partie de la variable PATH. Les bibliothèques et les .mod sont aussi cherchés dans un endroit conventionnel, sans avoir besoin de connaître les divers répertoires de compilation.
  • Avoir une version "stable" installée et continuer à développer et compiler ailleurs la version en développement.

  • Ne garder que le nécessaire. Possibilité de détruire après installation les répertoires source et de compilation. Les fichiers installés sont en général beaucoup moins nombreux que ceux du répertoire de compilation (fichiers objets, fichiers générés par CMake etc.).

Chemins d'installation conventionnels

  • /usr/local pour les installations communes à tous les utilisateurs (si on a l'autorisation).
  • ~/.local pour soi-même seulement, est un choix assez courant.

D'autres choix sont possibles.

Le chemin à choisir ci-dessus est la racine d'une arborescence. En dessous, on trouve les répertoires :

  • bin pour les exécutables
  • lib pour les bibliothèques
  • include pour les fichiers inclus (surtout en C ou C++) et les fichiers .mod
  • man, fichiers lus par la commande man
  • libexec pour les exécutables non destinés à être appelés directement

Installation d'un exécutable

Ajouter dans CMakeLists.txt la commande :

install(TARGETS plouf)

pour installer la cible plouf. À la configuration, choisir la racine de l'arborescence d'installation avec la variable de cache prédéfinie CMAKE_INSTALL_PREFIX. L'exécutable sera installé dans le sous-répertoire bin sous cette racine. Ni le répertoire désigné par CMAKE_INSTALL_PREFIX ni ses sous-répertoires (bin, etc.) n'ont besoin d'exister à l'avance.

Pour installer :

make install

Manipulation 27

Ajouter dans le projet Coriolis une commande d'installation. Tester.

Installation d'une bibliothèque : install(TARGETS)

install(TARGETS ma_bibli EXPORT ${PROJECT_NAME}Targets INCLUDES DESTINATION include)

déclare :

  • que la cible ma_bibli indiquée doit être installée lors de la commande make install
  • que la cible fait partie d'un ensemble de cibles à installer appelé ${PROJECT_NAME}Targets (on pourrait choisir ici un nom arbitraire)

  • que le sous-répertoire include de CMAKE_INSTALL_PREFIX doit être ajouté à la propriété INTERFACE_INCLUDE_DIRECTORIES de la cible installée.

Installation d'une bibliothèque : le fichier .mod

install(FILES ${PROJECT_BINARY_DIR}/ma_bibli.mod TYPE INCLUDE)

déclare que le fichier ma_bibli.mod doit être installé, que ce fichier est de type INCLUDE et doit donc aller dans le répertoire correspondant de CMAKE_INSTALL_PREFIX.

Installation d'une bibliothèque : le fichier config, sans dépendances

install(EXPORT ${PROJECT_NAME}Targets DESTINATION lib/cmake/${PROJECT_NAME} NAMESPACE ${PROJECT_NAME}:: FILE ${PROJECT_NAME}Config.cmake)

demande la création, lors de make install, d'un fichier ${PROJECT_NAME}Config.cmake, dans le répertoire lib/cmake/${PROJECT_NAME} sous CMAKE_INSTALL_PREFIX. Ce fichier est destiné à find_package et permet de générer les cibles avec toutes leurs propriétés d'interface. Les cibles concernées sont celles qui font partie de ${PROJECT_NAME}Targets (ensemble de cibles défini par l'option EXPORT de install(TARGETS)). Les cibles générées auront le préfixe spécifié après l'option NAMESPACE.

Installation d'une bibliothèque : le fichier config, avec dépendances

install(EXPORT ${PROJECT_NAME}Targets DESTINATION lib/cmake/${PROJECT_NAME} NAMESPACE ${PROJECT_NAME}::)

La seule différence avec le cas avec dépendances est l'absence de l'option FILE. Le nom du fichier est alors le nom par défaut : ${PROJECT_NAME}Targets.cmake. Ce fichier sera créé, lors de make install, dans le répertoire lib/cmake/${PROJECT_NAME} sous CMAKE_INSTALL_PREFIX. Il sera inclus dans ${PROJECT_NAME}Config.cmake.

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION lib/cmake/${PROJECT_NAME})

Rappel : le fichier ${PROJECT_NAME}Config.cmake a été créé par configure_file.

Utilisation d'une bibliothèque dans le répertoire de compilation

Même si la bibliothèque est installée, on peut vouloir utiliser la bibliothèque dans son répertoire de compilation (par exemple pour utiliser la bibliothèque compilée en mode DEBUG, alors que celle installée est compilée en mode RELEASE, ou pour tester une version en développement). Nous avons vu que l'utilisation dans le répertoire de compilation était possible grâce à la commande export(TARGETS).

Si on a ajouté dans CMakeLists.txt des commandes d'installation, il est plus cohérent de remplacer export(TARGETS) par export(EXPORT).

export(EXPORT)

Cas sans dépendances, remplacer :

export(TARGETS target NAMESPACE ${PROJECT_NAME}:: FILE ${PROJECT_NAME}Config.cmake)

par :

export(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}:: FILE ${PROJECT_NAME}Config.cmake)

Cas avec dépendances, remplacer :

export(TARGETS target NAMESPACE ${PROJECT_NAME}:: FILE ${PROJECT_NAME}Targets.cmake)

par :

export(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}::)

(sans option FILE)

${PROJECT_NAME}Targets a été défini par l'option EXPORT de install(TARGETS).

La commande export(EXPORT) a pour effet, comme export(TARGETS), de créer ${PROJECT_NAME}Config.cmake (cas sans dépendances) ou ${PROJECT_NAME}Targets.cmake (cas avec dépendances) dans le répertoire de compilation.

Installation d'une bibliothèque : BUILD_INTERFACE

La commande :

target_include_directories(nr_util INTERFACE ${PROJECT_BINARY_DIR})

qui permet l'utilisation de la bibliothèque englobée, ou non installée, n'est plus possible si des commandes d'installation sont présentes. On ne peut ajouter ${PROJECT_BINARY_DIR} à l'interface de la cible installée : c'est un chemin absolu dans le répertoire de compilation. Solution :

target_include_directories(nr_util INTERFACE $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>)

$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}> vaut ${PROJECT_BINARY_DIR} pour les commandes de compilation et rien pour les commandes d'installation.

Manipulation 28

  • Ajouter les commandes d'installation aux bibliothèques NR_util, Jumble et Numer_Rec_95. Installer les bibliothèques.
  • Tester l'utilisation des bibliothèques installées avec le programme Coriolis.

Module regroupeur d'une bibliothèque

Une bibliothèque peut proposer un module unique d'interface avec l'utilisateur, qui ne fait que regrouper les interfaces des modules de la bibliothèque :

module ma_bibli
   use foo_m
   use bar_m
   use plouf_m
end module ma_bibli

Dans le programme utilisateur de la bibliothèque, le seul module utilisé est le regroupeur :

use ma_bibli, only: foo

Lors de la compilation de la bibliothèque, des fichiers .mod vont être créés pour les modules contenant les procédures et pour le module regroupeur.

Fichiers .mod d'une bibliothèque nécessaires au programme utilisateur

  • Quels fichiers .mod sont nécessaires à la compilation du programme utilisateur ? Seulement le .mod regroupeur, ma_bibli.mod, ou tous les .mod de la bibliothèque ?
  • La réponse dépend du compilateur (et de sa version). Avec gfortran 9 et ifort 15, le fichier .mod regroupeur contient toute l'information, lui seul est utile. Avec ifort 19, nagfor 6 et pgfortran 2016, tous les .mod doivent être présents.

Conséquences pour le CMakeLists.txt de la bibliothèque

  • Le fait que la compilation du programme utilisateur d'une bibliothèque puisse nécessiter l'accès à tous les .mod de la bibliothèque n'apporte pas de nouvelle contrainte si la bibliothèque est utilisée dans son répertoire de compilation : la propriété INTERFACE_INCLUDE_DIRECTORIES de la bibliothèque, qui permet de trouver le module regroupeur, permet aussi de trouver dans le même répertoire les autres .mod.

  • Par contre, c'est une nouvelle contrainte si la bibliothèque est utilisée dans son répertoire d'installation : tous les .mod doivent avoir été installés, pas seulement le .mod regroupeur.

Installation de tous les .mod d'une bibliothèque

Créer tous les .mod dans un répertoire séparé :

set_target_properties(target PROPERTIES Fortran_MODULE_DIRECTORY ${PROJECT_BINARY_DIR}/modules)

Ajouter ce répertoire à la propriété INCLUDE_DIRECTORIES de la bibliothèque dans le répertoire de compilation :

target_include_directories(target PUBLIC $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/modules>)

au lieu de :

target_include_directories(target INTERFACE $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>)

Installer le répertoire dans un sous-répertoire de include (plutôt que de mettre en vrac dans include tous les .mod de toutes les bibliothèques) :

install(DIRECTORY ${PROJECT_BINARY_DIR}/modules/DESTINATION include/${PROJECT_NAME})

au lieu de :

install(FILES ${PROJECT_BINARY_DIR}/ma_bibli.mod TYPE INCLUDE)

Nota bene : le / après modules dans la commande install(DIRECTORY) ci-dessus, pour copier le contenu du répertoire et non le répertoire lui-même.

Indiquer que include/${PROJECT_NAME} est dans la propriété INTERFACE_INCLUDE_DIRECTORIES de la cible installée :

install(TARGETS target EXPORT ${PROJECT_NAME}Targets INCLUDES DESTINATION include/${PROJECT_NAME})

au lieu de :

install(TARGETS target EXPORT ${PROJECT_NAME}Targets INCLUDES DESTINATION include)

Possibilité d'ajouter dans CMakeLists.txt un test pour décider selon le compilateur si on installe seulement le .mod regroupeur ou tous les .mod.

Manipulation 29

Modifier les CMakeLists.txt des bibliothèques pour que l'installation et l'utilisation des bibliothèques installées avec les compilateurs NAG, Intel récent ou PGI soient possibles.