CMake: A quoi sert find_package () si vous avez besoin de spécifier CMAKE_MODULE_PATH de toute façon?

J’essaie de faire fonctionner un système de construction multi-plateformes en utilisant CMake. Maintenant, le logiciel a quelques dépendances. Je les ai compilés moi-même et les ai installés sur mon système.

Quelques exemples de fichiers installés:

-- Installing: /usr/local/share/SomeLib/SomeDir/somefile -- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile -- Installing: /usr/local/lib/SomeLib/somesharedlibrary -- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake -- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake 

Maintenant, CMake a un find_package() qui ouvre un fichier Find*.cmake et qui recherche la bibliothèque du système et définit certaines variables comme SomeLib_FOUND etc.

Mon CMakeLists.txt contient quelque chose comme ceci:

 set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}") find_package(SomeLib REQUIRED) 

La première commande définit où CMake effectue des recherches après le Find*.cmake et j’ai ajouté le répertoire de SomeLibFindSomeLib.cmake peut être trouvé, donc find_package() fonctionne comme prévu.

Mais c’est un peu étrange, car l’une des raisons pour lesquelles find_package() existe est de s’éloigner des chemins codés en dur non croisés.

Comment cela se fait-il habituellement? Dois-je copier le répertoire cmake/ de SomeLib dans mon projet et définir le CMAKE_MODULE_PATH relativement?

La commande find_package a deux modes: le mode Module et le mode Config . Vous essayez d’utiliser le mode Module lorsque vous avez réellement besoin du mode Config .

Mode module

Find.cmake fichier Find.cmake situé dans votre projet. Quelque chose comme ça:

 CMakeLists.txt cmake/FindFoo.cmake cmake/FindBoo.cmake 

Contenu CMakeLists.txt :

 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES include_directories("${FOO_INCLUDE_DIR}") include_directories("${BOO_INCLUDE_DIR}") add_executable(Bar Bar.hpp Bar.cpp) target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES}) 

Notez que CMAKE_MODULE_PATH a une priorité élevée et peut être utile lorsque vous devez réécrire le fichier standard Find.cmake .

Mode config (install)

Config.cmake Fichier Config.cmake situé à l’ extérieur et produit par la commande install d’un autre projet ( Foo par exemple).

foo library:

 > cat CMakeLists.txt cmake_minimum_required(VERSION 2.8) project(Foo) add_library(foo Foo.hpp Foo.cpp) install(FILES Foo.hpp DESTINATION include) install(TARGETS foo DESTINATION lib) install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo) 

Version simplifiée du fichier de configuration:

 > cat FooConfig.cmake add_library(foo STATIC IMPORTED) find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../") set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}") 

Par projet par défaut installé dans le répertoire CMAKE_INSTALL_PREFIX :

 > cmake -H. -B_builds > cmake --build _builds --target install -- Install configuration: "" -- Installing: /usr/local/include/Foo.hpp -- Installing: /usr/local/lib/libfoo.a -- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake 

Mode de configuration (utilisation)

Utilisez find_package(... CONFIG) pour inclure FooConfig.cmake avec la cible imscope:

 > cat CMakeLists.txt cmake_minimum_required(VERSION 2.8) project(Boo) # import library target `foo` find_package(Foo CONFIG REQUIRED) add_executable(boo Boo.cpp Boo.hpp) target_link_libraries(boo foo) > cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON > cmake --build _builds Linking CXX executable Boo /usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a 

Notez que la cible imscope est hautement configurable. Voir ma réponse

Mettre à jour

  • Exemple

Comment cela se fait-il habituellement? Dois-je copier le répertoire cmake/ de SomeLib dans mon projet et définir le CMAKE_MODULE_PATH relativement?

Si vous ne faites pas confiance à CMake pour avoir ce module, alors – oui, faites-le . C’est ce que je fais comme solution de rechange.

Notez que les modules FindFoo.cmake sont chacun une sorte de pont entre la dépendance à la plate-forme et l’indépendance de la plate-forme – ils recherchent des chemins dans des variables dont les noms sont indépendants de la plate-forme.

Vous n’avez pas besoin de spécifier le chemin du module en soi. CMake est livré avec son propre ensemble de scripts find_package intégrés, et leur emplacement est défini par défaut sur CMAKE_MODULE_PATH.

Le cas d’utilisation plus normal des projets dépendants ayant été CMakeified serait d’utiliser la commande external_project de CMake, puis d’inclure le fichier Use [Project] .cmake du sous-projet. Si vous avez juste besoin du script Find [Project] .cmake, copiez-le du sous-projet et dans le code source de votre propre projet, et vous n’aurez pas besoin d’augmenter le CMAKE_MODULE_PATH pour trouver le sous-projet au niveau du système.

Si vous exécutez cmake pour générer vous-même SomeLib (par exemple dans le cadre d’une super-construction), envisagez d’utiliser le registre des packages utilisateur . Cela ne nécessite aucun chemin codé en dur et est multi-plateforme. Sous Windows (y compris mingw64), cela fonctionne via le registre. Si vous examinez comment la liste des préfixes d’installation est construite à l’aide du mode CONFIG de la commande find_packages () , vous verrez que le registre de packages utilisateur est l’un des éléments.

Brève comment faire

Associez les cibles de SomeLib dont vous avez besoin en dehors de ce projet externe en les ajoutant à un jeu d’export dans les fichiers CMakeLists.txt où elles sont créées:

 add_library(thingInSomeLib ...) install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib) 

Créez un fichier XXXConfig.cmake pour SomeLib dans son ${CMAKE_CURRENT_BUILD_DIR} et stockez cet emplacement dans le registre des packages utilisateur en ajoutant deux appels à export () vers CMakeLists.txt associé à SomeLib :

 export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake export(PACKAGE SomeLib) # Store location of SomeLibConfig.cmake 

find_package(SomeLib REQUIRED) votre commande find_package(SomeLib REQUIRED) dans le fichier CMakeLists.txt du projet qui dépend de SomeLib sans le bricolage des “chemins non codés sur plusieurs plates-formes” avec CMAKE_MODULE_PATH .

Quand cela pourrait être la bonne approche

Cette approche est probablement la mieux adaptée aux situations où vous n’utiliserez jamais votre logiciel en aval du répertoire de construction (par exemple, vous effectuez une compilation croisée et n’installez jamais rien sur votre machine, ou vous construisez le logiciel uniquement pour exécuter des tests sur votre ordinateur). le répertoire de construction), car il crée un lien vers un fichier .cmake dans votre sortie “build”, qui peut être temporaire.

Mais si vous n’installez jamais SomeLib dans votre stream de travail, l’appel d’ EXPORT(PACKAGE ) vous permet d’éviter le chemin codé en dur. Et, bien sûr, si vous installez SomeLib , vous connaissez probablement votre plate-forme, CMAKE_MODULE_PATH , etc., donc l’excellente réponse de @ user2288008 vous couvrira.