Cmake无法使用“link_directories”找到库

66

我在使用Ubuntu操作系统,正在学习cmake和make,并尝试一个简单的例子。我有两个目录:srcbuild。在src中,我有两个文件:main.cppCMakeLists.txt,它只包含以下文本:

add_executable(test main.cpp)
link_directories(/usr/lib/x86_64-linux-gnu)
target_link_libraries(test protobuf)

在路径 /usr/lib/x86_64-linux-gnu 下有一个共享库称为 libprotobuf.so,我想链接它。我的 main.cpp 使用了这个库中的函数,包含相关的头文件,#include <google/protobuf/message.h>

现在,在我的 build 目录下,我运行 cmake ../src,然后运行 make。但是,我得到一些链接错误,告诉我 protobuf 库中某些函数有未定义的引用。如果我在 CMakeLists.txt 文件中删除 link_directories 这一行,而是在指定可执行文件时写入库的完整路径,即 target_link_libraries(test /usr/lib/x86_64-linux-gnu/libprotobuf.so),它就可以编译和链接成功。

为什么 link_directories 不能让 cmake 找到这个库呢?


5
CMake手册关于link_libraries命令的说明是:“该命令只适用于在其之后创建的目标。” 这里的目标是指使用add_executable()命令创建的吗?也许您需要将add_executable()命令移到link_directories()之后。 - Alain
1
如果我这样做,就会出现错误:“无法为未由此项目构建的目标“test”指定链接库。” - Karnivaurus
2
为了确保:您的命令顺序现在是1)link_directories(),2)add_executable(),3)target_link_libraries()? - Alain
是的,在那个顺序中 - 并且它不会将libprotobuf.so添加到由cmake生成的任何文件中。 - Karnivaurus
3
请使用find_package(Protobuf)命令来定位Protobuf库,不要直接尝试寻找它。有关更多信息,请参阅http://www.cmake.org/cmake/help/v3.3/module/FindProtobuf.html。 - tamas.kenez
1
如果find_package()对您不起作用,下一个最好的选择是使用find_library(PROTOBUF protobuf /usr/lib/x86_64-linux-gnu),然后在target_link_libraries()命令中使用${PROTOBUF}。在网络上搜索,您会看到许多人强烈建议不要使用link_directories()。即使是CMake文档似乎也推荐使用find_library()。 - Alain
4个回答

94

确保在调用相关的 add_executable 之前调用 link_directories

我之前错误地认为它只需要在调用 target_link_libraries 之前,但事实并非如此。将这个调用放到正确的位置后,库文件被正确链接了。


1
谢谢您的回答!我有一个FindSomething.cmake文件,它使用link_directories。实际上,当使用target_link_libraries将静态库与库“Something”链接,然后将该静态库与二进制程序链接时,链接器无法链接库,因为link_directories没有被考虑在内... - Aminos
1
这是问题的正确答案。关键词是 before your call ... - daparic

91

不要在CMake中这样使用link_directories,这是初学者常犯的错误。虽然在许多其他构建环境中都可以这样使用,但在CMake中这只会引起麻烦。即使官方文档也明确建议不要这样做:

请注意,这个命令 [link_directories] 很少需要使用。由find_package()find_library()返回的库位置是绝对路径。将这些绝对库文件路径直接传递给 target_link_libraries() 命令。CMake 将确保链接器找到它们。

因此,总是向target_link_libraries传递绝对路径,并使用find_library来解析链接目录:

find_library(PROTOBUF_LIBRARY protobuf HINTS /usr/lib/x86_64-linux-gnu)
target_link_libraries(test PUBLIC ${PROTOBUF_LIBRARY})

这样做的好处在于,如果在CMake配置时找不到期望的库,你很可能会得到诊断信息,而不是在编译时出现随机的链接错误。此外,这允许用户通过GUI指定库的位置,如果目标机器具有非标准的目录布局。

因此,如果一开始无法正常工作,请确保检查find_library调用的结果,并参考官方文档了解为什么无法按照预期找到您的库。


30
但是,为什么不起作用呢?我知道有人不建议这么做,但是它不应该还是可以起作用的吗? - aardvarkk
如果您使用pkg_check_modules查找库,则已经拥有所需的所有信息,因此使用pkg_check_modules是有意义的。 - wojciii
5
@aardvarkk 这份文件说:“该命令仅适用于在其被调用后创建的目标。” - landerlyoung
提醒:在Windows操作系统中,如果库文件在一个子目录中,则可能需要将 /* 添加到提示中。 - Den-Jason

9

确保顺序为link_directories、set PROJECT_LINK_LIBS、add_executable,最后是target_link_libraries。

以下示例说明:

cmake_minimum_required(VERSION 2.8.9)
project (Logging)
include_directories(include)
file(GLOB LOGGINGSOURCES "libsrc/*.cpp")
file(GLOB SOURCES "src/*.cpp")
add_library(convertString SHARED ${LOGGINGSOURCES})
install(TARGETS convertString DESTINATION /root/Deepak/)
link_directories( /root/Deepak/ )
set(PROJECT_LINK_LIBS libconvertString.so)
add_executable(hello ${SOURCES})
target_link_libraries(hello ${PROJECT_LINK_LIBS} )

1

也许这是一个非常老的话题,但是没有提出的解决方案适用于我。所以我不得不自己动手做一些肮脏的hack。我使用buildroot进行交叉编译,并包含toolchainfile.cmake

#...
set(LIB_PATH ${PROJECT_SOURCE_DIR}/relative/path/to/your/lib)
#...
include_directories(/path/to/library/include)
set(LIB_MYLIB ${LIB_PATH}/libmylib.so)
#...
add_executable(${PROJECT_NAME} ${APP_SOURCES})
target_link_libraries(${PROJECT_NAME}
        ${LIB_MYLIB}
)

希望这能有所帮助。

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