CMake lien vers une bibliothèque externe

126

Comment demander à CMake de lier un exécutable à une bibliothèque partagée externe qui n'est pas construite dans le même projet CMake?

Faire juste target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so)donne l'erreur

make[2]: *** No rule to make target `res/mylib.so', needed by `GLBall'.  Stop.
make[1]: *** [CMakeFiles/GLBall.dir/all] Error 2
make: *** [all] Error 2
(GLBall is the executable)

après avoir copié la bibliothèque dans le répertoire binaire bin/res.

J'ai essayé d'utiliser find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)

Ce qui échoue avec RESULT-NOTFOUND.

Premier
la source

Réponses:

101

Définissez d'abord le chemin de recherche des bibliothèques:

LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/res)

Et puis fais juste

TARGET_LINK_LIBRARIES(GLBall mylib)
arrowd
la source
44
L'utilisation de link_directoriesest déconseillée, même dans sa propre documentation. Je pense qu'il serait préférable ici de résoudre l' find_libraryappel échoué dans la question d'origine, ou d'utiliser la solution de @ Andre.
Fraser
4
Je trouve que la cible de la bibliothèque «importée» est plus robuste, car elle cible l'emplacement de la bibliothèque particulière, en donnant simplement un chemin de recherche global. Voir la réponse d'André.
Mark Lakata
1
Vous devez toujours utiliser find_libraryet utiliser ce chemin au lieu de le coder en dur, cf. ma réponse .
usr1234567
121

La réponse de arrowdodger est correcte et préférée à plusieurs reprises. Je voudrais simplement ajouter une alternative à sa réponse:

Vous pouvez ajouter une cible de bibliothèque "importée" au lieu d'un répertoire de liens. Quelque chose comme:

# Your-external "mylib", add GLOBAL if the imported library is located in directories above the current.
add_library( mylib SHARED IMPORTED )
# You can define two import-locations: one for debug and one for release.
set_target_properties( mylib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/res/mylib.so )

Et puis liez comme si cette bibliothèque avait été construite par votre projet:

TARGET_LINK_LIBRARIES(GLBall mylib)

Une telle approche vous donnerait un peu plus de flexibilité: jetez un œil à la commande add_library () et aux nombreuses propriétés cibles liées aux bibliothèques importées .

Je ne sais pas si cela résoudra votre problème avec les "versions mises à jour des bibliothèques".

André
la source
2
Ce serait probablement add_library( mylib SHARED IMPORTED )ou vous obtenez une add_library called with IMPORTED argument but no library typeerreur
Marvin
4
@Andre: Je pense qu'après IMPORTED_LOCATIONle bracketing d' ouverture, c'est faux
Ela782
5
vous devez ajouter GLOBALaprès IMPORTEDsi vous voulez accéder à la bibliothèque importée dans les répertoires au-dessus du courant:add_library(breakpad STATIC IMPORTED GLOBAL)
Roman Kruglov
@Andre IMPORTED_LOCATION semble exiger le chemin d'accès au fichier au lieu du répertoire contenant le fichier
SOUser
1
@SOUser: Oui, IMPORTED_LOCATION doit pointer vers le fichier, pas vers le répertoire. J'ai corrigé cela, je suppose que l'auteur ne se plaindra pas.
Tsyvarev
65

Je suppose que vous souhaitez créer un lien vers une bibliothèque appelée foo , son nom de fichier est généralement quelque chose de lien foo.dllou libfoo.so.

1. Trouvez la bibliothèque
Vous devez trouver la bibliothèque. C'est une bonne idée, même si vous connaissez le chemin d'accès à votre bibliothèque. CMake affichera une erreur si la bibliothèque a disparu ou a obtenu un nouveau nom. Cela permet de détecter rapidement l'erreur et de faire comprendre à l'utilisateur (peut-être vous-même) ce qui cause un problème.
Pour trouver une bibliothèque foo et stocker le chemin FOO_LIButilisé

    find_library(FOO_LIB foo)

CMake déterminera lui-même comment le nom réel du fichier est. Il vérifie les endroits habituels comme /usr/lib, /usr/lib64et les chemins d'accès PATH.

Vous connaissez déjà l'emplacement de votre bibliothèque. Ajoutez-le à CMAKE_PREFIX_PATHlorsque vous appelez CMake, puis CMake recherchera également votre bibliothèque dans les chemins passés.

Parfois, vous devez ajouter des astuces ou des suffixes de chemin, consultez la documentation pour plus de détails: https://cmake.org/cmake/help/latest/command/find_library.html

2. Liez la bibliothèque À partir de 1. vous avez le nom complet de la bibliothèque FOO_LIB. Vous l'utilisez pour lier la bibliothèque à votre cible GLBallcomme dans

  target_link_libraries(GLBall PRIVATE "${FOO_LIB}")

Vous devez ajouter PRIVATE, PUBLICou INTERFACEaprès la cible, cf. la documentation: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Si vous n'ajoutez pas l'un de ces spécificateurs de visibilité, il se comportera comme PRIVATEou PUBLIC, selon la version de CMake et les stratégies définies.

3. Ajouter des inclusions (Cette étape n'est peut-être pas obligatoire.)
Si vous souhaitez également inclure des fichiers d'en-tête, utilisez find_pathsimilaire à find_libraryet recherchez un fichier d'en-tête. Ajoutez ensuite le répertoire d'inclusion avec un target_include_directoriesfichier similaire à target_link_libraries.

Documentation: https://cmake.org/cmake/help/latest/command/find_path.html et https://cmake.org/cmake/help/latest/command/target_include_directories.html

Si disponible pour le logiciel externe, vous pouvez remplacer find_libraryet find_pathpar find_package.

usr1234567
la source
5
IMHO c'est la meilleure réponse. Cependant, j'ai eu du mal parce que je n'appelais pas "find_library" après "project" et "target_link_libraries" après "add_executable".
smoothware
1
find_packageest tellement plus simple que de suivre ces étapes
activedecay
2
Je suppose que je ne comprends pas l'étape 2. Pour une bibliothèque partagée, $ {FOO_LIB} sera comme /full/path/to/libfoo.dylib. En quoi est-ce utile? target_link_libraries ne crée pas "-L / full / path / to -lfoo", donc find_library ne renvoie rien d'utile, à part vérifier que la bibliothèque se trouve à l'emplacement où je le sais déjà. Qu'est-ce que je rate?
guymac
target_link_libraries(mylib "${FOO_LIB}")? La cible est au myliblieu de sa cible réelle, GLBall? Cela n'a pas beaucoup de sens pour moi
Bersan
5

Une autre alternative, dans le cas où vous travaillez avec l'Appstore, vous avez besoin de "droits" et en tant que tel vous devez vous connecter avec un Apple-Framework.

Pour que les droits fonctionnent (par exemple GameCenter), vous devez avoir une étape de construction "Lier le binaire avec les bibliothèques", puis créer un lien avec "GameKit.framework". CMake "injecte" les bibliothèques à un "bas niveau" dans la ligne de commande, donc Xcode ne le sait pas vraiment , et en tant que tel, vous ne le serez pas GameKit activé dans l'écran Capabilities.

Une façon d'utiliser CMake et d'avoir une étape de construction "Lien avec les binaires" est de générer le xcodeproj avec CMake, puis d'utiliser 'sed' pour 'rechercher et remplacer' et ajouter le GameKit comme XCode l'aime ...

Le script ressemble à ceci (pour Xcode 6.3.1).

s#\/\* Begin PBXBuildFile section \*\/#\/\* Begin PBXBuildFile section \*\/\
    26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks \*\/ = {isa = PBXBuildFile; fileRef = 26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/; };#g

s#\/\* Begin PBXFileReference section \*\/#\/\* Begin PBXFileReference section \*\/\
    26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System\/Library\/Frameworks\/GameKit.framework; sourceTree = SDKROOT; };#g

s#\/\* End PBXFileReference section \*\/#\/\* End PBXFileReference section \*\/\
\
\/\* Begin PBXFrameworksBuildPhase section \*\/\
    26B12A9F1C10543B00A9A2BA \/\* Frameworks \*\/ = {\
        isa = PBXFrameworksBuildPhase;\
        buildActionMask = 2147483647;\
        files = (\
            26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks xxx\*\/,\
        );\
        runOnlyForDeploymentPostprocessing = 0;\
    };\
\/\* End PBXFrameworksBuildPhase section \*\/\
#g

s#\/\* CMake PostBuild Rules \*\/,#\/\* CMake PostBuild Rules \*\/,\
            26B12A9F1C10543B00A9A2BA \/\* Frameworks xxx\*\/,#g
s#\/\* Products \*\/,#\/\* Products \*\/,\
            26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/,#g

enregistrez ceci dans "gamecenter.sed" puis "appliquez-le" comme ceci (cela change votre xcodeproj!)

sed -i.pbxprojbak -f gamecenter.sed myproject.xcodeproj/project.pbxproj

Vous devrez peut-être modifier les commandes de script pour répondre à vos besoins.

Attention: il est susceptible de rompre avec une version Xcode différente car le format du projet pourrait changer, le numéro unique (codé en dur) peut ne pas vraiment être unique - et généralement les solutions d'autres personnes sont meilleures - donc à moins que vous n'ayez besoin de soutenir l'Appstore + Droits (et builds automatisés), ne faites pas cela.

Il s'agit d'un bogue CMake, voir http://cmake.org/Bug/view.php?id=14185 et http://gitlab.kitware.com/cmake/cmake/issues/14185

Kalmiya
la source
Plus précisément, obtenir le lien entre cmake et une bibliothèque externe n'est pas le problème (il existe plusieurs solutions ci-dessus). Faire en sorte que cela fonctionne de manière automatisée, afin qu'il fonctionne avec l'Appstore Apple et les droits d'accès est un défi. Dans ce cas précis, les solutions ci-dessus ne fonctionnent pas car XCode ne «verra» pas les bibliothèques liées de cette manière, et les droits ne fonctionneront tout simplement pas. Afaik cmake ne peut pas ajouter les bibliothèques de la manière dont xcode en a besoin d'une `` manière compatible avec l'appstore '' - encore une fois, n'hésitez pas à m'éclairer.
kalmiya
1
Oh c'est triste. Pour être complet, le lien vers le nouveau suivi des problèmes, qui ne contient actuellement aucun commnets: gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567
Le problème a été résolu il y a 5 mois, donc avec une version récente de CMake, il ne devrait plus être présent. Voir gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567