CMake链接外部库

179

如何使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,意味着没有找到库文件。

5个回答

167

arrowdodger的回答在许多情况下是正确且首选的。我只想补充一个他的回答的替代方案:

你可以添加一个“imported”库目标,而不是链接目录。类似于:

# 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(IMPORTED) 命令和与导入库相关的 许多目标属性

我不知道这是否能解决你遇到的“库更新版本”的问题。


3
可能需要这样写: add_library(mylib SHARED IMPORTED),否则会出现add_library called with IMPORTED argument but no library type错误。 - Marvin
4
我认为在 IMPORTED_LOCATION 后面,开括号写错了。 - Ela782
6
如果您想在当前目录以上的目录中访问已导入的库,则需要在IMPORTED之后添加GLOBAL。示例代码如下:add_library(breakpad STATIC IMPORTED GLOBAL) - Roman Kruglov
1
@SOUser:是的,IMPORTED_LOCATION 应该指向文件,而不是目录。我已经修复了,猜测作者不会抱怨。 - Tsyvarev
1
我真的不知道为什么官方明确不支持这些基本用法!谢谢。 - Albert.Qing
显示剩余3条评论

125

首先设置库搜索路径:

link_directories(${CMAKE_BINARY_DIR}/res)

然后只需执行以下操作

target_link_libraries(GLBall mylib)

58
即使在其自身的文档中,link_directories 的使用也不被鼓励。我认为在这里最好解决原问题中失败的 find_library 调用,或者使用 @Andre 的解决方案。 - Fraser
5
我认为“导入”库目标更加健壮,因为它以特定库的位置为目标,而不仅是提供全局搜索路径。请参考安德烈的回答。 - Mark Lakata
2
你应该始终使用find_library并使用此路径,而不是硬编码它,参见我的回答 - usr1234567
大多数人来这里只是想让-lm工作。他们甚至不知道库的名称。 - mckenzm

114

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

1. 查找库
你需要找到这个库。即使你知道库的路径,这也是一个好主意。如果该库消失或更改名称,CMake将报错。这有助于及早发现错误,并清楚地告诉用户(可能是你自己)问题的根源。
要查找名为foo的库并将其路径存储在FOO_LIB中,请使用:

    find_library(FOO_LIB foo)

CMake会自动确定实际文件名的位置。它会检查像/usr/lib/usr/lib64PATH中指定的路径等通常的位置。

您已经知道库的位置。在调用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

如果没有指定这些可见性限定词,则会根据 CMake 版本和配置进行默认处理,可能表现为PRIVATEPUBLIC

第三步:添加头文件 (这一步不是必需的)
如果想要包含头文件,请类比find_library使用find_path查找头文件。然后使用target_include_directories添加包含目录,类比于target_link_libraries

文档: https://cmake.org/cmake/help/latest/command/find_path.htmlhttps://cmake.org/cmake/help/latest/command/target_include_directories.html

如果外部软件支持,可以将find_libraryfind_path替换为find_package


9
在我看来,这是最好的答案。但是,我遇到了困难,因为我没有在“project”之后调用“find_library”,也没有在“add_executable”之后调用“target_link_libraries”。 - smoothware
3
“find_package”比遵循这些步骤要简单得多。 - activedecay
3
我想我不理解第二步。对于一个共享库,${FOO_LIB} 的格式应该是 /full/path/to/libfoo.dylib。那有什么用呢?target_link_libraries 没有创建“-L/full/path/to -lfoo”,所以 find_library 返回的信息没有任何用处,除了验证我已经知道的库在那个位置。我错过了什么吗? - guymac
1
target_link_libraries(mylib "${FOO_LIB}")?目标是mylib而不是实际的目标GLBall?这对我来说没有太多意义。 - Bersan

14

假设你有一个像这样的可执行文件:

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


你的答案对我很有帮助!虽然是不同的用例,但在尝试了你的方法之前,我真的非常困扰 :) - Seaver
我可以安全地假设mylib.a -> mylib吗? - David C.
如果您没有指定 --static,@david-c ld 会更喜欢 mylib.so 而不是 mylib.a。 - tmt

5

如果您正在使用Appstore,需要“授权”,并且因此需要与Apple-Framework链接,那么还有另一种选择。

要使授权正常工作(例如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

您可能需要更改脚本命令以适应您的需求。

警告:随着项目格式的更改,(硬编码的)唯一编号可能不是真正的唯一编号,并且通常其他人提供的解决方案更好 - 因此,除非您需要支持Appstore +权限(和自动化构建),否则不要这样做,因为不同的Xcode版本可能会导致其崩溃。

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


具体来说,让cmake链接一个外部库并不是问题(上面提供了几种解决方案)。但让它自动化地与苹果应用商店以及权限一起使用就有难度了。在这种特殊情况下,上述解决方案无法奏效,因为XCode不会以那种方式‘看到’链接的库,而且权限也会失效。据我所知,cmake无法以符合‘应用商店兼容性’的方式添加库——欢迎指教。 - kalmiya
1
哦,那很遗憾。为了完整起见,这是新问题跟踪器的链接,目前还没有评论:https://gitlab.kitware.com/cmake/cmake/issues/14185 - usr1234567
问题已经在5个月前解决了,因此使用最新版本的CMake应该不再存在。请参见https://gitlab.kitware.com/cmake/cmake/issues/14185。 - usr1234567
具体来说,已经在CMake 3.19中修复。 - usr1234567

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接