Какой смысл использовать 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 превосходный ответ вас охватит.