CMake 链接到外部库

Posted

技术标签:

【中文标题】CMake 链接到外部库【英文标题】:CMake link to external library 【发布时间】:2012-01-08 01:16:04 【问题描述】:

如何让 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 失败。

【问题讨论】:

【参考方案1】:

arrowdodger 的答案在很多情况下都是正确且首选的。我只是想为他的答案添加一个替代方案:

您可以添加“导入”库目标,而不是链接目录。比如:

# 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( ) 命令和many target-properties related to imported libraries。

我不知道这是否会解决您关于“更新版本的库”的问题。

【讨论】:

那可能是add_library( mylib SHARED IMPORTED ) 或者你得到一个add_library called with IMPORTED argument but no library type 错误 @Andre:我认为IMPORTED_LOCATION之后的左括号是错误的 如果要访问当前目录上方的导入库,则需要在IMPORTED之后添加GLOBALadd_library(breakpad STATIC IMPORTED GLOBAL) @SOUser:是的,IMPORTED_LOCATION 应该指向文件,而不是目录。我已经解决了,估计作者不会抱怨。 我真的不知道为什么那些基本的用法官方没有明确支持!谢谢【参考方案2】:

先设置库搜索路径:

link_directories($CMAKE_BINARY_DIR/res)

然后就做

target_link_libraries(GLBall mylib)

【讨论】:

不鼓励使用link_directories,即使在它自己的文档中也是如此。我认为在这里解决原始问题中失败的find_library 调用会更好,或者使用@Andre 的解决方案。 我发现“导入的”库目标更加健壮,因为它针对特定库的位置,而不是简单地提供全局搜索路径。见安德烈的回答。 您应该始终使用find_library 并使用此路径而不是对其进行硬编码,参见。 my answer.【参考方案3】:

我假设你想链接到一个名为 foo 的库,它的文件名通常是链接 foo.dlllibfoo.so

1.查找图书馆 你必须找到图书馆。这是一个好主意,即使您知道库的路径。如果库消失或获得新名称,CMake 将出错。这有助于及早发现错误并让用户(可能是您自己)清楚导致问题的原因。 要查找库 foo 并将路径存储在 FOO_LIB 中,请使用

    find_library(FOO_LIB foo)

CMake 会自己弄清楚实际的文件名是怎样的。它会检查/usr/lib/usr/lib64 等常用位置以及PATH 中的路径。

您已经知道图书馆的位置。调用 CMake 时将其添加到 CMAKE_PREFIX_PATH,然后 CMake 也会在传递的路径中查找您的库。

有时您需要添加提示或路径后缀,详情请参阅文档: https://cmake.org/cmake/help/latest/command/find_library.html

2。链接库 从 1. 你在FOO_LIB 中有完整的库名称。您可以使用它将库链接到您的目标GLBall,如

  target_link_libraries(GLBall PRIVATE "$FOO_LIB")

您应该在目标之后添加PRIVATEPUBLICINTERFACE,参见。文档: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

如果您不添加这些可见性说明符之一,它的行为将类似于 PRIVATEPUBLIC,具体取决于 CMake 版本和策略集。

3.添加包含 (此步骤可能不是强制性的。) 如果您还想包含头文件,请使用类似于find_libraryfind_path 并搜索头文件。然后添加与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_libraryfind_path 替换为find_package

【讨论】:

恕我直言,这是最好的答案。但是,我遇到了麻烦,因为我没有在“project”之后调用“find_library”,在“add_executable”之后没有调用“target_link_libraries”。 find_package 比按照这些步骤简单得多 我想我不明白第 2 步。对于共享库 $FOO_LIB 将类似于 /full/path/to/libfoo.dylib。这有什么用? target_link_libraries 没有创建“-L/full/path/to -lfoo”,所以 find_library 没有返回任何有用的东西,除了验证库是否在我已经知道的位置。我错过了什么? target_link_libraries(mylib "$FOO_LIB")?目标是mylib 而不是他的实际目标GLBall?对我来说没有多大意义【参考方案4】:

另一种选择,如果您正在使用 Appstore,则需要“权利”,因此需要与 Apple 框架链接。

要使授权正常工作(例如 GameCenter),您需要具有“将二进制文件与库链接”-构建步骤,然后与“GameKit.framework”链接。 CMake 将“低级别”的库“注入”到命令行中,因此 Xcode 并不真正了解它,因此您将启用 GameKit功能屏幕。

使用 CMake 并拥有“与二进制文件链接”的一种方法是使用 CMake 生成 xcodeproj,然后使用“sed”来“搜索和替换”并以 XCode 喜欢的方式添加 GameKit。 .

脚本如下所示(对于 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 + Entitlements(和自动构建),不要这样做。

这是一个 CMake 错误,请参阅 http://cmake.org/Bug/view.php?id=14185 和 http://gitlab.kitware.com/cmake/cmake/issues/14185

【讨论】:

特别 - 让 cmake 与外部库链接不是问题(上面有几种解决方案)。让它以自动化的方式工作,以便它与 Apple Appstore 权利一起工作是一个挑战。在这种特定情况下,上述解决方案不起作用,因为 XCode 不会“看到”以这种方式链接的库,并且权利将不起作用。 Afaik cmake 无法以“与应用商店兼容的方式”以 xcode 需要的方式添加库 - 再次,请随时启发我。 哦,这很可悲。为了完整起见,新问题跟踪器的链接目前不包含任何通信:gitlab.kitware.com/cmake/cmake/issues/14185 问题已在 5 个月前解决,因此对于最新版本的 CMake,它应该不再存在。见gitlab.kitware.com/cmake/cmake/issues/14185 在 CMake 3.19 中修复,具体来说。【参考方案5】:

假设您有一个可执行文件,例如:

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

【讨论】:

您的回答对我有用!这是一个不同的用例,但在我尝试你的方式之前我真的很卡住:) 假设 mylib.a -> mylib 是否安全?

以上是关于CMake 链接到外部库的主要内容,如果未能解决你的问题,请参考以下文章

注册。使用 CMake 将外部库链接到项目

cmake 将外部库与 IMPORT_SONAME ro IMPORT_LOCATION 链接

CMake--模块的使用和自定义模块

在 CMake 中链接外部预建库 [重复]

使用 CMake 只构建一次外部库

在运行时链接外部库