CMake enlace a biblioteca externa

126

¿Cómo hacer que CMake vincule un ejecutable a una biblioteca compartida externa que no está construida dentro del mismo proyecto CMake?

Solo hacer target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so)da el error

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)

después de copiar la biblioteca en el directorio binario bin/res.

Traté de usar find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)

Lo cual falla con RESULT-NOTFOUND.

principal
fuente

Respuestas:

101

Establecer primero la ruta de búsqueda de bibliotecas:

LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/res)

Y luego solo haz

TARGET_LINK_LIBRARIES(GLBall mylib)
arrowd
fuente
43
Se link_directoriesdesaconseja su uso, incluso en su propia documentación. Creo que sería mejor aquí resolver la find_libraryllamada fallida en la pregunta original, o usar la solución de @ Andre.
Fraser
44
Considero que el objetivo de la biblioteca "importada" es más robusto, ya que apunta a la ubicación de la biblioteca en particular, en lugar de simplemente dar una ruta de búsqueda global. Ver la respuesta de Andre.
Mark Lakata
1
Siempre debe usar find_libraryy usar esta ruta en lugar de codificarla, cf. mi respuesta .
usr1234567
121

La respuesta de arrowdodger es correcta y preferida en muchas ocasiones. Simplemente me gustaría agregar una alternativa a su respuesta:

Puede agregar un objetivo de biblioteca "importado", en lugar de un directorio de enlaces. Algo como:

# 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 )

Y luego enlace como si su biblioteca fuera construida por su proyecto:

TARGET_LINK_LIBRARIES(GLBall mylib)

Tal enfoque le daría un poco más de flexibilidad: eche un vistazo al comando add_library () y las muchas propiedades de destino relacionadas con las bibliotecas importadas .

No sé si esto resolverá su problema con las "versiones actualizadas de libs".

André
fuente
2
Probablemente sea add_library( mylib SHARED IMPORTED )eso o obtendrá un add_library called with IMPORTED argument but no library typeerror
Marvin
44
@Andre: Creo que después IMPORTED_LOCATIONdel corchete de apertura está mal
Ela782
55
debe agregar GLOBALdespués IMPORTEDsi desea acceder a la biblioteca importada en directorios superiores al actual:add_library(breakpad STATIC IMPORTED GLOBAL)
Roman Kruglov
@Andre IMPORTED_LOCATION parece requerir la ruta al archivo en lugar del directorio que contiene el archivo
SOUser
1
@SOUser: Sí, IMPORTED_LOCATION debería apuntar al archivo, no al directorio. He arreglado eso, supongo que el autor no se quejará.
Tsyvarev
64

Supongo que desea vincular a una biblioteca llamada foo , su nombre de archivo generalmente es algo vinculado foo.dllo libfoo.so.

1. Encuentra la biblioteca
Tienes que encontrar la biblioteca. Esta es una buena idea, incluso si conoce el camino a su biblioteca. CMake generará un error si la biblioteca se desvaneció u obtuvo un nuevo nombre. Esto ayuda a detectar el error temprano y dejar en claro al usuario (puede ser usted mismo) qué causa un problema.
Para encontrar una biblioteca foo y almacenar la ruta en FOO_LIBuso

    find_library(FOO_LIB foo)

CMake descubrirá por sí mismo cómo es el nombre real del archivo. Comprueba los lugares habituales como /usr/lib, /usr/lib64y los caminos en PATH.

Ya sabes la ubicación de tu biblioteca. Agréguelo a CMAKE_PREFIX_PATHcuando llame a CMake, luego CMake buscará su biblioteca en las rutas pasadas también.

A veces necesita agregar sugerencias o sufijos de ruta, consulte la documentación para obtener más detalles: https://cmake.org/cmake/help/latest/command/find_library.html

2. Enlace la biblioteca de 1. tiene el nombre completo de la biblioteca FOO_LIB. Usas esto para vincular la biblioteca a tu objetivo GLBallcomo en

  target_link_libraries(GLBall PRIVATE "${FOO_LIB}")

Debe agregar PRIVATE, PUBLICo INTERFACEdespués del objetivo, cf. la documentación: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Si no agrega uno de estos especificadores de visibilidad, se comportará como PRIVATEo PUBLIC, según la versión de CMake y el conjunto de políticas.

3. Agregar incluye (este paso puede no ser obligatorio).
Si también desea incluir archivos de encabezado, use find_pathsimilar find_libraryy busque un archivo de encabezado. Luego agregue el directorio de inclusión con target_include_directoriessimilar a target_link_libraries.

Documentación: https://cmake.org/cmake/help/latest/command/find_path.html y https://cmake.org/cmake/help/latest/command/target_include_directories.html

Si está disponible para el software externo, puede reemplazar find_libraryy find_pathpor find_package.

usr1234567
fuente
44
En mi humilde opinión, esta es la mejor respuesta. Sin embargo, tuve problemas porque no estaba llamando "find_library" después de "project" y "target_link_libraries" después de "add_executable".
smoothware
1
find_packagees mucho más simple que seguir estos pasos
activedecay
2
Supongo que no entiendo el paso 2. Para una biblioteca compartida, $ {FOO_LIB} será como /full/path/to/libfoo.dylib. ¿Cómo es eso útil? target_link_libraries no está creando "-L / full / path / to -lfoo", por lo que find_library no está devolviendo nada útil, aparte de verificar que la biblioteca está en la ubicación donde ya sé que está. ¿Qué me estoy perdiendo?
guymac
target_link_libraries(mylib "${FOO_LIB}")? El objetivo myliben lugar de su objetivo real, GLBall? no tiene mucho sentido para mí
Bersan
5

Una alternativa más, en el caso de que esté trabajando con la tienda de aplicaciones, necesita "Derechos" y, como tal, necesita vincularse con un Apple-Framework.

Para que los derechos funcionen (por ejemplo, GameCenter), debe tener un "Enlace binario con bibliotecas" -buildstep y luego enlazar con "GameKit.framework". CMake "inyecta" las bibliotecas en un "nivel bajo" en la línea de comandos, por lo tanto, Xcode realmente no lo sabe y, como tal, no habilitará GameKit en la pantalla Capacidades.

Una forma de usar CMake y tener un "Enlace con binarios" -buildstep es generar el xcodeproj con CMake, y luego usar 'sed' para 'buscar y reemplazar' y agregar el GameKit de la forma en que a XCode le gusta ...

El script se ve así (para 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

guarda esto en "gamecenter.sed" y luego "aplícalo" así (¡cambia tu xcodeproj!)

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

Es posible que tenga que cambiar los comandos de script para satisfacer sus necesidades.

Advertencia: es probable que se rompa con diferentes versiones de Xcode, ya que el formato del proyecto podría cambiar, el número único (codificado) podría no ser realmente único, y en general las soluciones de otras personas son mejores, por lo que a menos que necesite apoyar la tienda de aplicaciones + Derechos (y compilaciones automatizadas), no hagas esto.

Este es un error de CMake, consulte http://cmake.org/Bug/view.php?id=14185 y http://gitlab.kitware.com/cmake/cmake/issues/14185

kalmiya
fuente
Específicamente: hacer que cmake se vincule con una biblioteca externa no es el problema (hay varias soluciones anteriores). Hacer que esto funcione de manera automatizada, de modo que funcione con Apple Appstore y los derechos es un desafío. En ese caso específico, las soluciones anteriores no funcionan porque XCode no "verá" las bibliotecas vinculadas de esa manera, y los derechos simplemente no funcionarán. Afaik cmake no puede agregar las librerías de la forma en que xcode lo necesita de una manera compatible con la tienda de aplicaciones, nuevamente, siéntase libre de iluminarme.
kalmiya
1
Oh eso es triste. Para completar el enlace al nuevo rastreador de problemas, que actualmente no contiene commnets: gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567
El problema se resolvió hace 5 meses, por lo que con una versión reciente de CMake ya no debería estar presente. Ver gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567