¿Cuál es la forma correcta de usar `pkg-config` desde` cmake`?

81

Mirando a mi alrededor en la red, he visto mucho código como este:

include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)

target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS}
target_link_libraries(app ${SDL2_LIBRARIES})

Sin embargo, esa parece ser la forma incorrecta de hacerlo, ya que solo usa los directorios y bibliotecas de inclusión, pero ignora las definiciones, las rutas de las bibliotecas y otros indicadores que podrían ser devueltos por pkg-config.

¿Cuál sería la forma correcta de hacer esto y asegurarse de que todos los indicadores de compilación y enlace devueltos por pkg-configsean utilizados por el compilado app? ¿Y hay un solo comando para lograr esto, es decir, algo como target_use(app SDL2)?

árbitro:

Grumbel
fuente

Respuestas:

28

Si está usando cmake y pkg-config de una manera bastante normal, esta solución funciona.

Sin embargo, si tiene una biblioteca que existe en algún directorio de desarrollo (como / home / me / hack / lib), el uso de otros métodos que se muestran aquí no configura las rutas del enlazador. Las bibliotecas que no se encuentran en las ubicaciones de instalación típicas darían lugar a errores del vinculador, como /usr/bin/ld: cannot find -lmy-hacking-library-1.0. Esta solución corrige el error del vinculador para ese caso.

Otro problema podría ser que los archivos pkg-config no están instalados en el lugar normal y las rutas pkg-config para el proyecto deben agregarse utilizando la variable de entorno PKG_CONFIG_PATH mientras cmake se está ejecutando (consulte otras preguntas de Stack Overflow al respecto). Suponiendo que tenga configurada la ruta correcta de pkg-config, esta solución también soluciona ese problema.

La solución se reduce a esta versión final de un CMakeLists.txt funcional:

cmake_minimum_required(VERSION 3.14)
project(ya-project C)

# the `pkg_check_modules` function is created with this call
find_package(PkgConfig REQUIRED) 

# these calls create special `PkgConfig::<MODULE>` variables
pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)

add_executable(program-name file.c ya.c)

target_link_libraries(program-name PUBLIC
        PkgConfig::MY_PKG
        PkgConfig::YOUR_PKG)

Tenga en cuenta que target_link_librarieshace más que cambiar los comandos del vinculador. También propaga otras propiedades PÚBLICAS de objetivos especificados como: indicadores del compilador, definiciones del compilador, rutas de inclusión, etc.

decaimiento activo
fuente
5
IMPORTED_TARGETrequiere CMake 3.6 o más reciente.
Cris Luengo
Si votó negativamente, asegúrese de comentar por qué votó negativamente para que podamos mejorar la respuesta.
activedecay
62

Primero de, la llamada:

include(FindPkgConfig)

debe ser reemplazado por:

find_package(PkgConfig)

La find_package()llamada es más flexible y permite opciones como REQUIRED, que hacen cosas automáticamente con las que uno tendría que hacer manualmente include().

En segundo lugar, las llamadas manuales pkg-configdeben evitarse cuando sea posible. CMake viene con un amplio conjunto de definiciones de paquetes, que se encuentran en Linux en /usr/share/cmake-3.0/Modules/Find*cmake. Estos brindan más opciones y opciones para el usuario que una llamada sin procesar pkg_search_module().

En cuanto al target_use()comando hipotético mencionado , CMake ya lo tiene incorporado de alguna manera con PUBLIC | PRIVATE | INTERFACE. Una llamada como target_include_directories(mytarget PUBLIC ...)hará que los directorios de inclusión se utilicen automáticamente en cada destino que utilice mytarget, por ejemplo target_link_libraries(myapp mytarget). Sin embargo, este mecanismo parece ser solo para bibliotecas creadas dentro del CMakeLists.txtarchivo y no funciona para bibliotecas adquiridas conpkg_search_module() . La llamadaadd_library(bar SHARED IMPORTED) podría usarse para eso, pero aún no lo he investigado.

En cuanto a la pregunta principal, esto aquí funciona en la mayoría de los casos:

find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
...
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})

El SDL2_CFLAGS_OTHERcontiene define y otras banderas necesarios para una compilación exitosa. Las banderasSDL2_LIBRARY_DIRSSDL2_LDFLAGS_OTHERSin embargo, las y aún se ignoran, no tengo idea de la frecuencia con la que eso se convertiría en un problema.

Más documentación aquí http://www.cmake.org/cmake/help/v3.0/module/FindPkgConfig.html

Grumbel
fuente
4
Estoy de acuerdo en que pkg-config debe evitarse SI existe un Find * .cmake, pero ese todavía no es el caso para la última versión de cmake en 2016.
Cubic
3
Esto no funciona si las bibliotecas no están en los directorios predeterminados. link_directories () puede ser una solución alternativa, pero es global.
Henry Hu
Este enfoque no funciona para vcpkg . ¿Puedo localizar SDL2_image sin rutas de codificación rígida?
user2023370
@HenryHu, ¿podría mostrar cómo se haría esto si esta fuera la situación en la configuración en esta respuesta?
Tim Visée
2
Requerir una herramienta de compilación como CMake para agrupar heurísticas para rastrear todas las bibliotecas del mundo no tiene sentido, no es su función. Pkg-config está diseñado para que sea responsabilidad del autor de la lib o del mantenedor de pkg / distro ponerlo a disposición de los usuarios. Y si se sigue este esquema, la forma correcta de usar una lib es siempre llamando a pkg-config.
Johan Boulé
10

Es raro que solo sea necesario vincular con SDL2. La respuesta actualmente popular usa pkg_search_module()which verifica los módulos dados y usa el primero que funciona.

Es más probable que desee vincular con SDL2 y SDL2_Mixer y SDL2_TTF, etc ... pkg_check_modules()comprueba todos los módulos dados.

# sdl2 linking variables
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_ttf SDL2_mixer SDL2_image)

# your app
file(GLOB SRC "my_app/*.c")
add_executable(my_app ${SRC})
target_link_libraries(my_app ${SDL2_LIBRARIES})
target_include_directories(my_app PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(my_app PUBLIC ${SDL2_CFLAGS_OTHER})

Descargo de responsabilidad: simplemente habría comentado sobre la respuesta propia de Grumbel si tuviera suficientes créditos callejeros con stackoverflow.

fluxrider
fuente
2
Globular archivos de origen es una mala práctica y no se recomienda.
liberforce
1
Para mí, target_link_libraries(my_app ${SDL2_LINK_LIBRARIES})funcionó mejor.
stefan
2
@liberforce Globbing source files es una buena práctica, es culpa de CMake si tiene errores.
Johan Boulé
1
@ JohanBoulé: No, no lo es. Puede hacer que un desarrollador agregue un montón de archivos localmente y haga que las cosas funcionen en su computadora, y no comprometa todos los archivos necesarios. Luego empujan sus cambios y se rompe para otras personas. Claro, esto se puede detectar mediante una integración continua, pero este es solo el problema más obvio. El sistema de compilación Meson optó por no implementar el globbing de archivos , y los desarrolladores de CMake desalientan explícitamente el globbing . Explícito es mejor que implícito.
liberforce
1
@liberforce Ya he visto ese argumento muchas veces más que el problema real sobre el que teoriza. Meson en contra, build2 a favor. Ninguno lo tendrá, como tabulador vs espacio.
Johan Boulé
6

La mayoría de las respuestas disponibles no configuran los encabezados de la pkg-configbiblioteca. Después de meditar en la documentación de FindPkgConfig, se me ocurrió una solución que también proporciona:

include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
  message(FATAL_ERROR "pkg-config not found!" )
endif()
 
pkg_check_modules(<some-lib> REQUIRED IMPORTED_TARGET <some-lib>)
 
target_link_libraries(<my-target> PkgConfig::<some-lib>)

( Sustituya su objetivo en lugar de <my-target>y cualquier biblioteca en lugar de <some-lib>, según corresponda. )

La IMPORTED_TARGETopción parece ser clave y hace que todo esté disponible en el PkgConfig::espacio de nombres. Esto era todo lo que se requería y también todo lo que debía requerirse.

Eero Aaltonen
fuente
SUGERENCIA: imprima cmake var después de ejecutar pkg_check_modulespara ver las vars disponibles stackoverflow.com/a/9328525/1211174
roble
0
  1. No existe un comando como target_use . Pero conozco varios proyectos que han escrito tal comando para su uso interno. Pero cada proyecto quiere pasar banderas o definiciones adicionales, por lo que no tiene sentido tenerlo en CMake general. Otra razón para no tenerlo son las bibliotecas con plantillas de C ++ como Eigen, no hay biblioteca, pero solo tiene un montón de archivos de inclusión.

  2. La forma descrita a menudo es correcta. Puede que sea diferente para algunas bibliotecas, luego tendrá que agregar _LDFLAGSo _CFLAGS. Una razón más para no tener target_use. Si no le funciona, haga una nueva pregunta específica sobre SDL2 o cualquier biblioteca que desee usar.

usr1234567
fuente
-2

Si también está buscando agregar definiciones de la biblioteca, las add_definitionsinstrucciones están ahí para eso. La documentación se puede encontrar aquí , junto con más formas de agregar indicadores del compilador.

El siguiente fragmento de código usa esta instrucción para agregar GTKGL al proyecto:

pkg_check_modules(GTKGL REQUIRED gtkglext-1.0)
include_directories(${GTKGL_INCLUDE_DIRS})
link_directories(${GTKGL_LIBRARY_DIRS})
add_definitions(${GTKGL_CFLAGS_OTHER})
set(LIBS ${LIBS} ${GTKGL_LIBRARIES})

target_link_libraries([insert name of program] ${LIBS})
Jeanluc
fuente
3
No use, include_directoriesetc. ¡infectará el alcance global! Use target_include_directoriesetc
Dawid Drozd