Какой смысл использовать find_package(), если вам все равно нужно указать CMAKE_MODULE_PATH?

Я пытаюсь создать систему сборки кросс-plattform, использующую CMake. Теперь у программного обеспечения есть несколько зависимостей. Я сам их скомпилировал и установил в своей системе.

Некоторые файлы примеров, которые были установлены:

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

Теперь у CMake есть find_package(), который открывает файл Find*.cmake и выполняет поиск после библиотеки в системе и определяет некоторые переменные, такие как SomeLib_FOUND и т.д.

My CMakeLists.txt содержит что-то вроде этого:

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

Первая команда определяет, где CMake ищет после Find*.cmake, и я добавил каталог SomeLib, где можно найти FindSomeLib.cmake, поэтому find_package() работает как ожидалось.

Но это довольно странно, потому что одна из причин, по которым существует find_package(), заключается в том, чтобы уйти от непересекающихся жестких кодированных путей.

Как это обычно делается? Должен ли я скопировать каталог cmake/ SomeLib в мой проект и установить CMAKE_MODULE_PATH относительно?

Ответ 1

Команда find_package имеет два режима: режим Module и Config. Вы пытаетесь используйте режим Module, когда вам действительно нужен режим Config.

Режим модуля

Find<package>.cmake файл расположен внутри вашего проекта. Что-то вроде этого:

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

CMakeLists.txt:

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

Обратите внимание, что CMAKE_MODULE_PATH имеет высокий приоритет и может быть полезен, когда вам нужно переписать стандартный Find<package>.cmake файл.

Режим конфигурации (установка)

<package>Config.cmake файл, расположенный снаружи и созданный install команда другого проекта (например, Foo).

Foo библиотека:

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

Упрощенная версия файла конфигурации:

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

По умолчанию проект установлен в директории 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

Режим конфигурации (использование)

Используйте find_package(... CONFIG) для включения FooConfig.cmake с импортированной целью 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

Обратите внимание, что импортированный целевой объект с высокой степенью настраивается. См. Мой ответ .

Обновление

Ответ 2

Вам не нужно указывать путь к модулю как таковой. CMake поставляется с собственным набором встроенных скриптов find_package, а их местоположение находится в CMAKE_MODULE_PATH по умолчанию.

Более нормальный вариант использования зависимых проектов, которые были обработаны CMake, будет заключаться в использовании команды CMake external_project, а затем включить файл Use [Project].cmake из подпроекта. Если вам просто нужен Find [Project].cmake script, скопируйте его из подпроекта и в свой собственный исходный код проекта, а затем вам не нужно увеличивать CMAKE_MODULE_PATH, чтобы найти подпроект на системном уровне.

Ответ 3

Как это обычно делается? Должен ли я скопировать каталог cmake/ SomeLib в мой проект и установить относительно CMAKE_MODULE_PATH?

Если вы не доверяете CMake иметь этот модуль, тогда - да, сделайте это - вроде: Скопируйте find_SomeLib.cmake и его зависимости в вашу директорию cmake/. Это то, что я делаю как запасной вариант. Это уродливое решение, хотя.

Обратите внимание, что FindFoo.cmake модулей FindFoo.cmake является своего рода мостом между зависимостью от платформы и независимостью от платформы - они ищут в различных местах, зависящих от платформы, чтобы найти пути в переменных, имена которых не зависят от платформы.

Ответ 4

Если вы запускаете cmake для самостоятельной генерации SomeLib (скажем, как часть супербилда), подумайте об использовании реестра пользовательских пакетов. Это не требует жестко закодированных путей и является кроссплатформенным. В Windows (включая mingw64) он работает через реестр. Если вы посмотрите, как список префиксов установки создается в режиме CONFIG команды find_packages(), вы увидите, что реестр пользовательских пакетов является одним из элементов.

Краткое руководство

SomeLib вам цели SomeLib вне этого внешнего проекта, добавив их в набор экспорта в файлах CMakeLists.txt где они создаются:

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

Создайте файл XXXConfig.cmake для SomeLib в его ${CMAKE_CURRENT_BUILD_DIR} и сохраните это местоположение в реестре пакетов пользователей, добавив два вызова export() в CMakeLists.txt связанный с SomeLib:

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

find_package(SomeLib REQUIRED) в файле CMakeLists.txt проекта, который зависит от SomeLib без " SomeLib жестко закодированных путей", связанных с CMAKE_MODULE_PATH.

Когда это может быть правильный подход

Этот подход, вероятно, лучше всего подходит для ситуаций, когда вы никогда не будете использовать свое программное обеспечение ниже каталога сборки (например, вы кросс-компилируете и никогда ничего не устанавливаете на своем компьютере, или вы создаете программное обеспечение только для запуска тестов в каталог сборки), поскольку он создает ссылку на файл .cmake в выходных данных "сборки", которые могут быть временными.

Но если вы на самом деле никогда не устанавливаете SomeLib в своем SomeLib, вызов EXPORT(PACKAGE <name>) позволяет вам избежать жестко заданного пути. И, конечно же, если вы устанавливаете SomeLib, вы, вероятно, знаете свою платформу, CMAKE_MODULE_PATH и т.д., Так что @user2288008 превосходный ответ вас охватит.