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

Как заставить 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.


person Prime    schedule 08.01.2012    source источник


Ответы (5)


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

link_directories(${CMAKE_BINARY_DIR}/res)

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

target_link_libraries(GLBall mylib)
person arrowd    schedule 08.01.2012
comment
Использование link_directories не рекомендуется, даже в собственной документации. Я думаю, что здесь было бы лучше разрешить неудачный вызов find_library в исходном вопросе или использовать решение @Andre. - person Fraser; 28.03.2013
comment
Я считаю, что цель импортированной библиотеки более надежна, поскольку она нацелена на расположение конкретной библиотеки, а не просто дает глобальный путь поиска. См. Ответ Андре. - person Mark Lakata; 19.03.2016
comment
Вы всегда должны использовать find_library и использовать этот путь вместо его жесткого кодирования, ср. мой ответ. - person usr1234567; 28.01.2017

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

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

# 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 () и многие целевые свойства, связанные с импортированными библиотеками.

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

person André    schedule 11.05.2012
comment
Вероятно, это будет add_library( mylib SHARED IMPORTED ) или вы получите add_library called with IMPORTED argument but no library type ошибку - person Marvin; 28.08.2012
comment
@Andre: Я думаю, что после IMPORTED_LOCATION открывающая скобка неверна - person Ela782; 06.11.2014
comment
вам нужно добавить GLOBAL после IMPORTED, если вы хотите получить доступ к импортированной библиотеке в каталогах выше текущей: add_library(breakpad STATIC IMPORTED GLOBAL) - person Roman Kruglov; 16.02.2016
comment
@Andre IMPORTED_LOCATION, похоже, требует путь к файлу вместо каталога, содержащего файл - person SOUser; 02.04.2017
comment
@SOUser: Да, IMPORTED_LOCATION должен указывать на файл, а не на каталог. Я исправил это, думаю, автор не будет жаловаться. - person Tsyvarev; 07.12.2017
comment
Я действительно не знаю, почему это базовое использование не поддерживается официальным языком! Спасибо - person Albert.Qing; 31.05.2019
comment
(target_link_libraries): Cannot specify link libraries for target "mylib" which is not built by this project. ninja: error: rebuilding 'build.ninja': subcommand failed Почему я получаю эту ошибку? может кто-нибудь помочь? - person 김선달; 18.03.2020

Я предполагаю, что вы хотите создать ссылку на библиотеку с именем 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.

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

Еще одна альтернатива: в случае, если вы работаете с 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

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

Допустим, у вас есть исполняемый файл вроде:

add_executable(GLBall GLBall.cpp)

Если у внешней библиотеки есть заголовки, укажите путь к ее подключаемой папке:

target_include_directories(GLBall PUBLIC "/path/to/include")

Добавьте путь к каталогу библиотеки:

target_link_directories(GLBall PUBLIC "/path/to/lib/directory")

Наконец, свяжите имя библиотеки

target_link_libraries(GLBall mylib)

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

libmylib.a ➜ mylib
mylib.so ➜ mylib

person Sorush    schedule 05.05.2021
comment
Ваш ответ сработал для меня! Это был другой вариант использования, но я действительно застрял, пока не попробовал ваш путь :) - person Seaver; 08.06.2021