web-dev-qa-db-fra.com

Quelle est l'utilisation de find_package () si vous devez quand même spécifier CMAKE_MODULE_PATH?

J'essaie de faire fonctionner CMake avec un système de construction multi-plateformes. 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 une find_package() qui ouvre un fichier Find*.cmake, recherche la bibliothèque sur le système et définit des variables telles que 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 ses recherches après le Find*.cmake et j'ai ajouté le répertoire de SomeLib où le FindSomeLib.cmake peut être trouvé, de sorte que 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 sans la plate-forme croisée.

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

134
MarcDefiant

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

Mode module

Find<package>.cmake fichier situé dans de votre projet. Quelque chose comme ça:

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

CMakeLists.txt contenu:

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

Mode de configuration (installation)

<package>Config.cmake fichier situé à l'extérieur et produit par la commande install d'un autre projet (Foo par exemple).

foo bibliothèque:

> 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 défaut, le projet 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 config (utilisation)

Utilisez find_package(... CONFIG) pour inclure FooConfig.cmake avec la cible importée foo:

> 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 importée est hautement configurable. Voir mon réponse .

Mettre à jour

172
user2288008

Si vous utilisez cmake pour générer SomeLib vous-même (par exemple, dans le cadre d'un super-build), envisagez d'utiliser le ser Package Registry . 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 par le mode CONFIG de la commande find_packages () , vous verrez que le registre de packages utilisateur est l'un des éléments.

bref comment faire

Associez les cibles de SomeLib dont vous avez besoin en dehors de ce projet externe en les ajoutant à un ensemble d'exportation dans les fichiers CMakeLists.txt où elles ont été 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 de packages utilisateur en ajoutant deux appels à export () dans le CMakeLists.txt associé à SomeLib:

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

Émettez votre commande find_package(SomeLib REQUIRED) dans le fichier CMakeLists.txt du projet qui dépend de SomeLib sans les "chemins codés en dur non multiplateformes" avec le CMAKE_MODULE_PATH.

Quand cela pourrait être la bonne approche

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

Mais si vous n'installez jamais réellement SomeLib dans votre flux de travail, l'appel de EXPORT(PACKAGE <name>) 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., de sorte que l'excellente réponse de @ user2288008 saura vous couvrir.

2
Ryan Feeley

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

Si vous ne faites pas confiance à CMake pour avoir ce module, alors - oui, faites-le - en quelque sorte: Copiez le find_SomeLib.cmake et ses dépendances dans votre répertoire cmake/. C'est ce que je fais comme solution de secours. C'est une solution laide cependant.

Notez que les modules FindFoo.cmake constituent chacun une sorte de pont entre dépendance à la plate-forme et indépendance de la plate-forme. Ils examinent différents emplacements spécifiques à la plate-forme pour obtenir des chemins dans des variables dont le nom est indépendant de la plate-forme.

1
einpoklum

Vous n'avez pas besoin de spécifier le chemin du module en tant que tel. CMake est livré avec son propre ensemble de scripts find_package intégrés et leur emplacement est l'emplacement par défaut CMAKE_MODULE_PATH.

Le cas d'utilisation plus normal pour les projets dépendants qui ont été CMakeified serait d'utiliser la commande external_project de CMake puis d'inclure le fichier. Use [Projet] .cmake du sous-projet. Si vous avez simplement besoin du script Find [Project] .cmake, copiez-le hors du sous-projet et dans le code source de votre propre projet. Vous n'aurez alors pas besoin d'étendre CMAKE_MODULE_PATH pour trouver le sous-projet au niveau du système.

1
zjm555