Ссылка CMake на внешнюю библиотеку

127

Как заставить CMake связать исполняемый файл с внешней общей библиотекой, которая не построена в том же проекте CMake?

Просто выполнение target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so)дает ошибку

make[2]: *** No rule to make target `res/mylib.so', needed by `GLBall'.  Stop.
make[1]: *** [CMakeFiles/GLBall.dir/all] Error 2
make: *** [all] Error 2
(GLBall is the executable)

после того, как я скопировал библиотеку в двоичный каталог bin/res.

Я пробовал использовать find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)

Что терпит неудачу с RESULT-NOTFOUND.

простое число
источник

Ответы:

102

Сначала установите путь поиска библиотек:

LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/res)

А потом просто сделай

TARGET_LINK_LIBRARIES(GLBall mylib)
arrowd
источник
44
Использование link_directoriesне рекомендуется даже в собственной документации. Я думаю, что здесь было бы лучше разрешить неудачный find_libraryвызов в исходном вопросе или использовать решение @Andre.
Fraser
4
Я считаю, что цель «импортированной» библиотеки более надежна, поскольку она нацелена на расположение конкретной библиотеки, а не просто дает глобальный путь поиска. См. Ответ Андре.
Марк Лаката,
1
Вы всегда должны использовать find_libraryи использовать этот путь вместо жесткого его кодирования, см. мой ответ .
usr1234567
121

Ответ стрелка верен и во многих случаях предпочтителен. Я просто хотел бы добавить к его ответу альтернативу:

Вы можете добавить "импортированную" библиотеку вместо каталога ссылок. Что-то вроде:

# Your-external "mylib", add GLOBAL if the imported library is located in directories above the current.
add_library( mylib SHARED IMPORTED )
# You can define two import-locations: one for debug and one for release.
set_target_properties( mylib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/res/mylib.so )

А затем сделайте ссылку, как если бы эта библиотека была создана вашим проектом:

TARGET_LINK_LIBRARIES(GLBall mylib)

Такой подход даст вам немного больше гибкости: обратите внимание на команду add_library () и множество целевых свойств, связанных с импортированными библиотеками .

Не знаю, решит ли это вашу проблему с «обновленными версиями библиотек».

Андре
источник
2
Вероятно, это будет, add_library( mylib SHARED IMPORTED )иначе вы получите add_library called with IMPORTED argument but no library typeошибку
Марвин
4
@Andre: Я думаю, что после IMPORTED_LOCATIONоткрытия скобка неверна
Ela782
5
вам нужно добавить GLOBALпосле, IMPORTEDесли вы хотите получить доступ к импортированной библиотеке в каталогах выше текущей:add_library(breakpad STATIC IMPORTED GLOBAL)
Роман Круглов
@Andre IMPORTED_LOCATION, похоже, требует путь к файлу вместо каталога, содержащего файл
SOUser 02
1
@SOUser: Да, IMPORTED_LOCATION должен указывать на файл, а не на каталог. Я исправил это, думаю, автор не будет жаловаться.
Цыварев 07
66

Я предполагаю, что вы хотите создать ссылку на библиотеку с именем foo , ее имя файла обычно является ссылкой foo.dllили libfoo.so.

1. Найдите библиотеку.
Вы должны найти библиотеку. Это хорошая идея, даже если вы знаете путь к своей библиотеке. CMake выдаст ошибку, если библиотека исчезла или получила новое имя. Это помогает обнаружить ошибку на раннем этапе и дать понять пользователю (а может и самому себе), что вызывает проблему.
Чтобы найти библиотеку Foo и сохранить путь в FOO_LIBиспользовании

    find_library(FOO_LIB foo)

CMake сам определит фактическое имя файла. Он проверяет обычные места, такие как /usr/lib, /usr/lib64и пути внутрь PATH.

Вы уже знаете, где находится ваша библиотека. Добавьте его CMAKE_PREFIX_PATHпри вызове CMake, тогда CMake также будет искать вашу библиотеку в переданных путях.

Иногда вам нужно добавить подсказки или суффиксы пути, подробности см. В документации: https://cmake.org/cmake/help/latest/command/find_library.html

2. Свяжите библиотеку с 1. у вас есть полное имя библиотеки FOO_LIB. Вы используете это, чтобы связать библиотеку с вашей целью, GLBallкак в

  target_link_libraries(GLBall PRIVATE "${FOO_LIB}")

Вы должны добавить PRIVATE , PUBLICили INTERFACEпосле цели, ср. документация: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Если вы не добавите один из этих описателей видимости, он будет вести себя как PRIVATEили PUBLIC, в зависимости от версии CMake и установленных политик.

3. Добавить включает (этот шаг может быть необязательным).
Если вы также хотите включить файлы заголовков, используйте find_pathаналогичные find_libraryи найдите файл заголовка. Затем добавьте каталог include с target_include_directoriesпохожими на target_link_libraries.

Документация: https://cmake.org/cmake/help/latest/command/find_path.html и https://cmake.org/cmake/help/latest/command/target_include_directories.html

Если доступно внешнее ПО, можно заменить find_libraryи find_pathна find_package.

usr1234567
источник
5
ИМХО это лучший ответ. Однако у меня были проблемы, потому что я не вызывал «find_library» после «project» и «target_link_libraries» после «add_executable».
smoothware
1
find_packageнамного проще, чем следовать этим шагам
activedecay
2
Я не понимаю шага 2. Для общей библиотеки $ {FOO_LIB} будет иметь вид /full/path/to/libfoo.dylib. Как это полезно? target_link_libraries не создает «-L / full / path / to -lfoo», поэтому find_library не возвращает ничего полезного, кроме проверки того, что библиотека находится в том месте, о котором я уже знаю. Что мне не хватает?
guymac
target_link_libraries(mylib "${FOO_LIB}")? Цель mylibвместо его реальной цели GLBall,? для меня не имеет особого смысла
Берсан
5

Еще одна альтернатива: в случае, если вы работаете с Appstore, требуются «Права» и, как таковые, необходимо связываться с Apple-Framework.

Для работы Права (например, GameCenter) вам необходимо создать шаг сборки «Связать двоичный файл с библиотеками», а затем связать его с «GameKit.framework». CMake «вставляет» библиотеки на «низком уровне» в командную строку , поэтому Xcode на самом деле не знает об этом, и поэтому вы не сможете включить GameKit на экране возможностей.

Один из способов использования CMake и шаг сборки «Связать с двоичными файлами» - это сгенерировать xcodeproj с помощью CMake, а затем использовать «sed» для «поиска и замены» и добавить GameKit так, как это нравится XCode ...

Скрипт выглядит так (для Xcode 6.3.1).

s#\/\* Begin PBXBuildFile section \*\/#\/\* Begin PBXBuildFile section \*\/\
    26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks \*\/ = {isa = PBXBuildFile; fileRef = 26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/; };#g

s#\/\* Begin PBXFileReference section \*\/#\/\* Begin PBXFileReference section \*\/\
    26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System\/Library\/Frameworks\/GameKit.framework; sourceTree = SDKROOT; };#g

s#\/\* End PBXFileReference section \*\/#\/\* End PBXFileReference section \*\/\
\
\/\* Begin PBXFrameworksBuildPhase section \*\/\
    26B12A9F1C10543B00A9A2BA \/\* Frameworks \*\/ = {\
        isa = PBXFrameworksBuildPhase;\
        buildActionMask = 2147483647;\
        files = (\
            26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks xxx\*\/,\
        );\
        runOnlyForDeploymentPostprocessing = 0;\
    };\
\/\* End PBXFrameworksBuildPhase section \*\/\
#g

s#\/\* CMake PostBuild Rules \*\/,#\/\* CMake PostBuild Rules \*\/,\
            26B12A9F1C10543B00A9A2BA \/\* Frameworks xxx\*\/,#g
s#\/\* Products \*\/,#\/\* Products \*\/,\
            26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/,#g

сохраните это в "gamecenter.sed", а затем "примените" вот так (это изменит ваш xcodeproj!)

sed -i.pbxprojbak -f gamecenter.sed myproject.xcodeproj/project.pbxproj

Возможно, вам придется изменить команды сценария в соответствии с вашими потребностями.

Предупреждение: он может сломаться с другой версией Xcode, поскольку формат проекта может измениться, (жестко запрограммированный) уникальный номер может не быть уникальным - и, как правило, решения других людей лучше - поэтому, если вам не нужно поддерживать Appstore + Права (и автоматические сборки) не делайте этого.

Это ошибка CMake, см. Http://cmake.org/Bug/view.php?id=14185 и http://gitlab.kitware.com/cmake/cmake/issues/14185

kalmiya
источник
В частности, заставить cmake связываться с внешней библиотекой не проблема (есть несколько решений выше). Заставить это работать автоматизированным способом, чтобы он работал с Apple Appstore и правами, является проблемой. В этом конкретном случае вышеупомянутые решения не работают, потому что XCode не «увидит» библиотеки, связанные таким образом, и права просто не будут работать. Afaik cmake не может добавлять библиотеки так, как это нужно xcode, «совместимым с appstore способом» - опять же, не стесняйтесь просветить меня.
Kalmiya
1
Ой, это грустно. Для полноты информации приведена ссылка на новую систему отслеживания проблем, которая в настоящее время не содержит сообщений: gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567
Проблема была решена 5 месяцев назад, поэтому в последней версии CMake ее больше не должно быть. См. Gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567