Яке використання find_package (), якщо вам все одно потрібно вказати CMAKE_MODULE_PATH?


167

Я намагаюся отримати систему зшивання платформ, яка працює за допомогою 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 має a, find_package()який відкриває Find*.cmakeфайл та шукає бібліотеку в системі та визначає деякі змінні, наприклад, SomeLib_FOUNDetc.

Мій 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відносно?


Ця картина здається мені дуже дивною. Бібліотеки, що використовують CMake, не повинні викривати модуль "знайти" таким чином. Як ви придумали такий спосіб знайти той "SomeLib"? А яка вона?
SirDarius

2
Щось подібне робиться в cmake.org/Wiki/… . І це ОГРЕ.
MarcDefiant

2
Розділ, на який ви посилаєтесь, згадує це: "Оскільки CMake (наразі) не доставляє його, вам доведеться відправити його у рамках свого проекту." Це я зробив у програмі flvmeta, щоб знайти LibYAML (див. Github.com/noirotm/flvmeta/tree/master/cmake/modules ). Шлях модуля вказує на цей каталог всередині мого проекту.
SirDarius

3
Зазвичай я копіюю модулі FindXXX у свій проект і встановлюю CMAKE_MODULE_PATH (якщо, звичайно, ці модулі відсутні в CMake), я також багато разів бачив цю схему в інших проектах
szx

Відповіді:


214

Команда 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

Зауважте, що імпортна ціль налаштована дуже легко . Дивіться мою відповідь .

Оновлення


1
Ваша відповідь чудова. Однак приклад на Github є більш складним, що він може бути ІМО. У загальному випадку, коли підкаталог (модуль) експортує один артефакт, скажімо, що lib разом із заголовками, вам не потрібно генерувати власні * Config.cmake. В результаті конфігурація може бути значно скорочена. Думаю, я сам зроблю подібний приклад.
Димитріс

2
@Dimitris Так, це можна трохи спростити. Я оновив приклад github, тому зараз він не використовується configure_package_config_file. До речі, якщо у вас є якісь інші пропозиції, ви можете надіслати мені запит.

1
@rusio Ось мій приклад . Він підтримує монолітну збірку (всі модулі з кореневої папки) або автономну збірку (кожен модуль окремо потребує встановлення).
Димитріс

1
@Dimitris Гаразд, зараз я бачу. Зазвичай файл, який ви «оптимізуєте», служить для завантаження додаткових матеріалів, таких як find_dependency . Я думаю, що це гарний шаблон для запуску, тому я буду тримати його, навіть якщо він фактично не використовується. Решта коду виглядає більш простою, оскільки вам не вистачає певної функціональності, як версія, експорт для dll, макет із bin/lib(спробуйте встановити виконуваний файл та запустити його на Windows). І простори імен виглядають дуже красиво, тому я їх також зберігатиму :) Також я додав monolithicзбірку.

1
Кожен твій приклад був мені дуже корисним. Дякую обом!
zmb

2

Якщо ви працюєте, cmakeщоб генерувати SomeLibсебе (скажімо, як частина надбудови), подумайте про використання Реєстру пакунків користувачів . Для цього не потрібні чітко кодовані контури і є платформою. У Windows (включаючи mingw64) він працює через реєстр. Якщо ви вивчите, як складається список префіксів установки за CONFIGрежимом команди find_packages () , ви побачите, що Реєстр пакунків користувачів є одним із елементів.

Коротка інструкція

Пов’яжіть цілі, SomeLibякі вам потрібні за межами цього зовнішнього проекту, додавши їх до набору експорту у CMakeLists.txtфайлах, де вони створені:

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

Створіть XXXConfig.cmakeфайл SomeLibу своєму ${CMAKE_CURRENT_BUILD_DIR}і збережіть це місце в Реєстрі пакунків користувачів, додавши два виклики для експорту () до 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без "непересічних платформних кодованих контурів", що торкаються CMAKE_MODULE_PATH.

Коли це може бути правильний підхід

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

Але якщо ви ніколи насправді не встановлюєте SomeLibу своєму робочому процесі, виклик EXPORT(PACKAGE <name>)дозволяє уникнути важко кодованого шляху. І, звичайно, якщо ви встановлюєте SomeLib, ви, напевно, знаєте свою платформу CMAKE_MODULE_PATHтощо, тому відмінна відповідь @ user2288008 вас охопить.


1

Не потрібно вказувати шлях модуля як такий. CMake поставляється із власним набором вбудованих скриптів find_package, і їх розташування знаходиться в стандартному CMAKE_MODULE_PATH.

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


12
their location is in the default CMAKE_MODULE_PATHза замовчуванням CMAKE_MODULE_PATHпорожній

Може підтвердити, що коментар @ user2288008 у 2018 році CMAKE_MODULE_PATHпорожній для Windows.
Єроен

Це спеціальна змінна для проекту модулів, що доставляють ваш проект. "За замовчуванням воно порожнє, воно повинно бути встановлене проектом." cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway

1

Як це зазвичай робиться? Чи потрібно скопіювати cmake/каталог SomeLib у свій проект і встановити CMAKE_MODULE_PATH відносно?

Якщо ви не довіряєте CMake мати цей модуль, тоді - так, зробіть це - сортування: Скопіюйте свої find_SomeLib.cmakeта його залежності у свій cmake/каталог. Ось що я роблю як резервний. Але це некрасиве рішення.

Зауважте, що FindFoo.cmakeмодулі - це свого роду міст між залежністю від платформи та незалежністю платформи - вони шукають у різних місцях, що залежать від платформи, щоб отримати шляхи в змінних, імена яких не залежать від платформи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.