¿De qué sirve find_package () si necesita especificar CMAKE_MODULE_PATH de todos modos?

167

Estoy tratando de hacer que un sistema de compilación multiplataforma funcione usando CMake. Ahora el software tiene algunas dependencias. Los compilé yo mismo y los instalé en mi sistema.

Algunos archivos de ejemplo que se instalaron:

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

Ahora CMake tiene un archivo find_package()que abre un Find*.cmakearchivo y busca la biblioteca en el sistema y define algunas variables comoSomeLib_FOUND etc.

Mi CMakeLists.txt contiene algo como esto:

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

El primer comando define dónde busca CMake después de Find*.cmakey agregué el directorio SomeLibdonde FindSomeLib.cmakese puede encontrar, por lo quefind_package() funciona como se esperaba.

Pero esto es un poco extraño porque una de las razones por las cuales find_package() existe es para alejarse de los caminos codificados sin plataforma cruzada.

¿Cómo se hace esto generalmente? ¿Debo copiar el cmake/directorio de SomeLiben mi proyecto y configurar el CMAKE_MODULE_PATHrelativamente?

MarcDefiant
fuente
Ese patrón me parece muy extraño. No se supone que las bibliotecas que usan CMake expongan su módulo 'find' de esta manera. ¿Cómo se te ocurrió tal manera de encontrar ese "SomeLib"? ¿Y qué lib es?
SirDarius
2
Algo similar se hace en cmake.org/Wiki/… . Y es OGRE.
MarcDefiant
2
La sección a la que enlaza menciona esto: "Dado que CMake (actualmente) no lo envía, tendrá que enviarlo dentro de su proyecto". Esto es lo que he hecho en flvmeta para encontrar LibYAML (ver github.com/noirotm/flvmeta/tree/master/cmake/modules ). La ruta del módulo apunta a este directorio, dentro de mi proyecto.
SirDarius
3
Normalmente copio los módulos FindXXX a mi proyecto y configuro CMAKE_MODULE_PATH (si esos módulos no están presentes en CMake, por supuesto), también he visto este patrón muchas veces en otros proyectos
szx

Respuestas:

214

El comando find_packagetiene dos modos: Modulemodo y Configmodo. Estás intentando usar el Modulemodo cuando realmente necesitas el Configmodo.

Modo de módulo

Find<package>.cmakearchivo ubicado dentro de su proyecto. Algo como esto:

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

CMakeLists.txt contenido:

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

Tenga en cuenta que CMAKE_MODULE_PATHtiene alta prioridad y puede ser útil cuando necesita reescribir un Find<package>.cmakearchivo estándar .

Modo de configuración (instalar)

<package>Config.cmakearchivo ubicado afuera y producido por install comando de otro proyecto (Foo por ejemplo).

foo biblioteca:

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

Versión simplificada del archivo de configuración:

> 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}")

Por defecto proyecto instalado en el CMAKE_INSTALL_PREFIXdirectorio:

> 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

Modo de configuración (uso)

Use find_package(... CONFIG)para incluir FooConfig.cmakecon destino importado 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

Tenga en cuenta que el objetivo importado es altamente configurable. Mira mi respuesta .

Actualizar

Comunidad
fuente
1
Tu respuesta es genial. Sin embargo, el ejemplo en github es más complejo de lo que puede ser IMO. En el caso común en el que un subdirectorio (módulo) exporta un solo artefacto, digamos una lib junto con los encabezados, no necesita generar * Config.cmake personalizado. Como resultado, la configuración puede reducirse significativamente. Creo que haré un ejemplo similar yo mismo.
Dimitris
2
@Dimitris Sí, se puede simplificar un poco. He actualizado el ejemplo de github, así que ahora no lo usa configure_package_config_file. Por cierto, si tiene alguna otra sugerencia, puede enviarme una solicitud de extracción.
1
@rusio Aquí está mi ejemplo . Admite compilación monolítica (todos los módulos de la carpeta raíz) o compilaciones autónomas (cada módulo por separado, requiere instalación).
Dimitris
1
@Dimitris Bien, ahora veo. Por lo general, el archivo que "optimiza" sirve para cargar cosas adicionales como find_dependency . Creo que es una buena plantilla para comenzar, así que la mantendré incluso si no se usa de hecho. El resto del código parece más simple porque le faltan algunas funciones como la versión, exportar para dll, diseño con bin/lib(intente instalar el ejecutable y ejecutarlo en Windows). Y los espacios de nombres se ven muy bonitos, así que los mantendré también :) También agregué monolithicbuild.
1
Cada uno de tus ejemplos fue muy útil para mí. ¡Gracias a los dos!
zmb
2

Si está ejecutando cmakepara generar SomeLibusted mismo (por ejemplo, como parte de una superconstrucción), considere usar el Registro de paquetes de usuario . Esto no requiere rutas codificadas y es multiplataforma. En Windows (incluido mingw64) funciona a través del registro. Si examina cómo se construye la lista de prefijos de instalación mediante el CONFIGmodo del comando find_packages () , verá que el Registro de paquetes de usuario es uno de los elementos.

Breve instrucciones

Asocia los objetivos SomeLibque necesitas fuera de ese proyecto externo agregándolos a un conjunto de exportación en los CMakeLists.txtarchivos donde se crean:

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

Cree un XXXConfig.cmakearchivo SomeLiben ${CMAKE_CURRENT_BUILD_DIR}y almacene esta ubicación en el Registro de paquetes de usuario agregando dos llamadas a export () a las CMakeLists.txtasociadas con SomeLib:

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

Emita su find_package(SomeLib REQUIRED)comando en el CMakeLists.txtarchivo del proyecto que depende SomeLibsin los "caminos codificados no multiplataforma" jugando con elCMAKE_MODULE_PATH .

Cuando podría ser el enfoque correcto

Este enfoque probablemente sea el más adecuado para situaciones en las que nunca usará su software aguas abajo del directorio de compilación (por ejemplo, está compilando de forma cruzada y nunca instala nada en su máquina, o está construyendo el software solo para ejecutar pruebas en el directorio de compilación), ya que crea un enlace a un archivo .cmake en su salida de "compilación", que puede ser temporal.

Pero si nunca está realmente instalando SomeLiben su flujo de trabajo, las llamadas le EXPORT(PACKAGE <name>)permiten evitar la ruta codificada. Y, por supuesto, si está instalando SomeLib, probablemente conozca su plataforma CMAKE_MODULE_PATH, etc., por lo que la excelente respuesta de @ user2288008 lo tendrá cubierto.

Ryan Feeley
fuente
1

No necesita especificar la ruta del módulo per se. CMake se entrega con su propio conjunto de scripts find_package integrados, y su ubicación está en el CMAKE_MODULE_PATH predeterminado.

El caso de uso más normal para proyectos dependientes que han sido CMakeified sería usar el comando external_project de CMake y luego incluir el archivo Use [Project] .cmake del subproyecto. Si solo necesita el script Find [Project] .cmake, cópielo del subproyecto y en el código fuente de su propio proyecto, y luego no necesitará aumentar el CMAKE_MODULE_PATH para encontrar el subproyecto a nivel del sistema.

zjm555
fuente
12
their location is in the default CMAKE_MODULE_PATHpor defecto CMAKE_MODULE_PATHestá vacío
Puede confirmar que el comentario de @ user2288008 en 2018 CMAKE_MODULE_PATHestá vacío en Windows.
Jeroen
Es una variable específica del proyecto, para los módulos que se envían con su proyecto. "Por defecto está vacío, está destinado a ser configurado por el proyecto". cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway
1

¿Cómo se hace esto generalmente? ¿Debo copiar el cmake/directorio de SomeLib en mi proyecto y establecer el CMAKE_MODULE_PATH relativamente?

Si no confía en que CMake tenga ese módulo, entonces, sí, haga eso: copie el find_SomeLib.cmakey sus dependencias en su cmake/directorio. Eso es lo que hago como reserva. Sin embargo, es una solución fea.

Tenga en cuenta que FindFoo.cmakecada uno de los módulos es una especie de puente entre la dependencia de la plataforma y la independencia de la plataforma: se ven en varios lugares específicos de la plataforma para obtener rutas en variables cuyos nombres son independientes de la plataforma.

einpoklum
fuente