【问题标题】:Why the linker is looking for non-needed libs为什么链接器正在寻找不需要的库
【发布时间】:2020-07-30 00:17:59
【问题描述】:

我有两个 C++ 模块:AB,其中 B 链接到一组静态库 lib*.a(我在这里使用 * 表示一组库文件)和 A链接到B

我有 B 的 CMakeLists.txt:

add_library(B STATIC B.cpp)
target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "/path/to/headers/directory/for/lib*.a")
link_directories("Path/to/directory/contains/lib*.a")
target_link_libraries(B PRIVATE lib*)

A 的 CMakeLists.txt:

add_library(A STATIC A.cpp)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "/path/to/headers/for/libB.a") # compiler outputs libB.a when target name is B
link_directories("Path/to/directory/contains/libB.a")
target_link_libraries(A PRIVATE B)

一切正常,直到我尝试将A 设为共享库,然后我从链接器收到错误消息cannot find -l*。我相信原因是当我将A设置为共享时,编译器会查找共享库,但不可用。

我不明白的事情:

  1. B 已经是静态库了,为什么链接器需要 lib*?
  2. 当我将A 设置为共享时,为什么编译器会查找共享库?我认为即使是共享的A 也可以链接到静态库

我确实想指出,在A.cpp 中,我在顶部有#include B.hh,而B.hh 还包括lib*.a 的标题,这就是我有target_include_directories(B PUBLIC...) 的原因

【问题讨论】:

  • target_include_directories /path/to/headers/for/lib*.a 如何是一个目录?为什么AlibB 链接而不与B 链接? link_directories("Path/to/libB.a") 为什么?
  • @KamilCuk 我更新了我的帖子。请看一下。关于 libB,编译器会自动将 lib 前缀附加到库名称中。所以如果你使用libB,cmake 会寻找libB,如果你使用B,cmake 仍然会寻找libB
  • A 的 CMakeLists.txt 中的 link_directories 没有理由。A 的 CMakeLists.txt 中的 target_include_directories 也是错误的。当 A 为 STATIC 时,它会创建一个存档文件。当您将 A 设为共享库时,它需要链接到 B 和所有 lib*,因为这就是共享库的工作方式。创建 B 时,它不包含来自 lib* 的任何内容,它只是 B 的任何来源的目标文件的存档。因此,涉及 B 的任何链接步骤也需要链接到 lib* 档案;假设您使用了正确的文件名。
  • @fdk1342 你的回答对解释我在这里遇到的情况很有意义,但它似乎与stackoverflow.com/questions/63163471/… 冲突,你能解释一下吗?谢谢!
  • 请不要在描述您的代码或错误消息时使用lib*.a 之类的东西。相反,请使用确切名称、路径等。您希望每个人都能理解这些“通配符”是什么类型的库,并且您希望减少“不需要的”(如您所想的)信息。但实际上你的整个描述是相当模糊,为了理解问题,我们需要猜测这种“globbing”背后的原因。这种猜测很难:对这种通配符的不同解释会极大地改变你的问题。请准备好minimal reproducible example,并发布确切代码和错误消息。

标签: c++ cmake


【解决方案1】:

您的初始设置已经无法正常工作,但在切换到共享库之前您没有看到错误。

这样做的原因是,只要它们都是静态库,您对 AB 的所有 target_link_libraries 依赖项实际上都不会被处理。这与静态库是什么有关:只是一堆目标文件打包到一个文件中。静态库是链接的,因此任何与链接器相关的构建依赖项(例如在其他静态库中链接为依赖项)都不会得到解决。构建系统会跟踪它们,但如果它们丢失,您不会注意到,除非您实际上将这些库传递给实际正在链接的东西 - 例如可执行文件,或者在您的情况下是现在,一个共享库。

由于像这样具有不可见的未解决依赖项可能有点令人尴尬,因此惯用的 CMake 将使您对事物进行不同的建模。您永远不会直接引入静态依赖项,而是使用如下方法:在最低层,您有一个 find_library 调用,用于查找系统上的任何外部预构建依赖项(在您的示例中为 lib*)。然后将其包装到导入的目标中,这样您就不必再次接触原始库路径。为了使事情顺利进行,您可以将所有这些隐藏在 find 包脚本中,这样您的代码只会看到提供 lib* 目标的 find_package 调用,而不必担心内部问题。

然后,您可以通过目标依赖项对所有库的相互依赖关系进行建模。这种方法有几个优点,使其更加健壮:如果 CMake 找不到 lib*,它会在 find_package 调用失败时立即告诉您,而不是等到您尝试像它那样链接现在。通过仅指定基于目标的依赖项,您可以确保构建实际上正在使用它配置的库(您现在使用的基于 link_directories 的方法无法确保这一点)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多