cmake распространяет зависимости, используя find_package - PullRequest
2 голосов
/ 08 января 2020

В качестве простого примера: есть две библиотеки и один исполняемый файл. Обе библиотеки SHARED (т.е. файл .so). Один называется libMain , а другой - libUtil . libMain использует libUtil , как и исполняемый файл. Исполняемый файл может использовать libUtil сам по себе, но обычно он вызывает метод из libMain , который в своей реализации использует libUtil .

Так что после прочтения некоторых уроков и документов по CMake этот пример показался довольно простым. Для каждого проекта существует простой CMakeLists.txt, в то время как libUtil ссылается на libMain и исполняемые ссылки на libMain . (Я пропустил target_include_directories, чтобы сэкономить несколько строк) Один и тот же PREFIX_PATH используется во всех проектах.

libUtil

include(GNUInstallDirs)

project(libUtil)
set(CMAKE_SHARED_LIBRARY_PREFIX "")    
add_library(libUtil SHARED main.cpp)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)

libMain

include(GNUInstallDirs)

project(libMain)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_library(libUtil SHARED main.cpp)
find_package(libUtil REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC libUtil)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)

исполняемый файл

project(exe)
find_package(libMain REQUIRED)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_executable(exe main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE libMain)

Однако при сборке исполняемого файла я обнаружил ошибку компоновщика, сообщив exe, что не может выполнить -llibUtil .

Я трачу на это довольно много времени уже и после читая несколько сообщений, которые я обнаружил (или, по крайней мере, мне так кажется), что мне нужно предоставить пользовательский libMainConfig.cmake (я переименовал сгенерированный в libMainTargets.cmake)

include("${CMAKE_CURRENT_LIST_DIR}/libMainTargets.cmake")

find_package(libUtil REQUIRED)

Ошибка компоновщика исчезла, и я могу запустить свой проект. До сих пор я не понимаю, зачем мне это делать. Я понял, что есть разница при работе с уже созданной библиотекой. Однако, поскольку CMake всегда использует разделение проектов на основе каталогов, как и зачем мне создавать несколько проектов в одном CMakeLists.txt? До сих пор я всегда использовал find_package (что обязательно делает add_library IMPORTED, то есть ищет библиотеку перед сборкой, если я не ошибаюсь) и хотел распространить ее определенные зависимости на «потребляющий» проект. Но кажется, что все PUBLIC, INTERFACE и PRIVATE из импортируемой цели не влияют на цель "потребления"?

Пожалуйста, подробно объясните в объяснении и / или укажите некоторые пропущенные ссылки и укажите некоторые рекомендации, предполагающие, что все проекты создаются самостоятельно или являются системными библиотеками, такими как boost или JNI.

Спасибо!

1 Ответ

3 голосов
/ 08 января 2020

CMake имеет специфицированную c функцию, предназначенную для распространения find_package, как это называется find_dependency. Вы можете использовать его следующим образом:

include(CMakeFindDependencyMacro)
find_dependency(libutil)

CMake не будет экспортировать вызовы find_package в созданные файлы. Вы должны сделать это вручную с помощью find_package или find_dependency. Разница в том, что find_dependency будет правильно пересылать REQUIRED и QUIET.

Почему CMake пытается отправить -llibUtil компоновщику?

При добавлении чего-либо в target_link_libraries , есть два случая:

  1. Библиотека для ссылки является целью. В этом случае свойства интерфейса распространяются правильно, включая ссылки, если это необходимо.
  2. Это просто строка, в которой нет целей. В этом случае CMake предполагает, что это системная библиотека, и добавляет флаг -l .

, поскольку ссылка на libutil является общедоступным c свойством libmain все потребители libmain будут также ссылаться на libutil.

Поскольку вызов пакета поиска должен выполняться вручную, libutil не является целью. Предполагается соединение с системной библиотекой, поэтому CMake сделает exe с системной библиотекой с именем libutil, которая не существует.

Вызов find_package(libUtil REQUIRED) обеспечит exe потребностью в использовании libutil связывая цели вместо библиотек.

Как libUtil может быть целью?

CMake цель (полностью) не привязана к каталогам. У вас может быть GLOBAL и IMPORTED целей, и вы можете иметь несколько целей по проектам. Представьте себе проект, который состоит из нескольких библиотек и исполняемых файлов. По аналогии можно сказать, что проекты CMake представляют собой решение Visual Studio, а цели CMake - проекты Visual Studio.

Теперь для импортированных целей. Они предназначены для представления целей, которые уже скомпилированы другим проектом, который вы либо установили, либо экспортировали в дерево сборки. Импортированная цель позволяет вам потреблять вещи из другого проекта так же, как обычная цель, как если бы вы объявили это. Импортируемые цели более точны, чем флаги компилятора: они несут, конечно, библиотеку, но также и то, как связать, необходимые флаги компилятора, включают каталоги и другие требования.

find_package и find_dependency предназначены для найти файл конфигурации, и они содержат информацию об импортированных целях. После этого, простая ссылка на него будет включать в себя каталоги, правильные флаги компоновщика и т. Д. c.

Хорошим примером проекта, экспортирующего несколько целей, является SFML, который экспортирует различные модули, такие как управление звуком, графикой и окнами. как отдельные статические / динамические c библиотеки.

Как можно избежать этой глупой ошибки?

CMake позволяет нам использовать пространства имен для целей. При использовании синтаксиса пространства имен он не может быть системной библиотекой. CMake выдаст ошибку, что цель не найдена, вместо попытки связать ее с несуществующей библиотекой.

install(
    EXPORT ${PROJECT_NAME}Config
    NAMESPACE libUtil
    DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake
)

И затем изменить ссылку на это:

target_link_libraries(${PROJECT_NAME} PUBLIC libUtil::libUtil)

Соглашение для имени пространства имен следует использовать то же имя, что и пакет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...