【问题标题】:Static libraries linked against other static libraries with CMake - one works, one doesn't. Why?使用 CMake 与其他静态库链接的静态库 - 一个有效,一个无效。为什么?
【发布时间】:2019-02-01 22:34:07
【问题描述】:

背景 我有一个使用其他较小项目的项目。这些项目本身是由其他项目组成的。其中很多是遗留问题或有其他管理命令的原因按原样安排,因此将所有内容整合到一个项目中不是一种选择。一些库是在远程共享上预编译的。

我有两个让我头疼的主要子项目:

  • Project Foo 是链接多个静态子项目(foo_subproject_1foo_subproject_n)的可执行文件和库。这些子项目进一步链接到远程位置的静态库(some_libsome_other_lib)。 Project Foo 的可执行文件可以正确编译、链接和运行

  • Project Bar 是一个可执行文件,可链接多个其他项目,包括 libFoo。链接失败,“未定义对”foo_subproject 函数的引用

据我所知,这两个项目的排列方式与它们的链接说明相似。看着 SO,我发现将静态库与静态库链接 shouldn't work,但后来我对如何成功编译 Project Foo 感到困惑。

gcc 和 g++ 4.9.2 是编译器(已经检查了 C 中的某些部分和 C++ 中的某些部分的外部“C”问题)


问题

我对 CMake add_subdirectory 的工作方式或链接器的工作方式中的一个或两个有误解。有人可以解释一下 Project Foo 是如何成功工作的,而 Project Bar(没有)按预期工作吗?

更新我仔细查看了 foo_lib.a 和 foo_runtime。

我应该从一开始就确定有什么问题,因为 foo_runtime 的大小接近 100MB,而 foo_lib 只有 10KB。

nm 表明 foo_lib.a 引用了几十个符号,其中大部分是未定义的。 foo_runtime 同时引用一切

同样令人困惑的是 foo_subproject_1.a 也大多是未定义的。同样,这是我期望看到的;但我不明白如何由此构建 foo_runtime?

我仍然不清楚为什么some_library -> subproject -> foo_runtime 成功,但some_library -> subproject -> foo_lib -> bar 没有。在我调查的这个阶段,我预计这两个命令都会失败。


Project Foo是这样安排的(使用CMake):

cmake_minimum_required(VERSION 2.6)
project(foo)

set(FOO_SRCS
    # source and headers for main foo project
)

# Project Foo libraries are subdirectories within this project
add_subdirectory(subs/foo_subproject_1)
add_subdirectory(subs/foo_subproject_2)

# Runtime executable
add_executable(foo_runtime main.c ${FOO_SRCS})
target_link_libraries(foo_runtime foo_subproject_1 foo_subproject_2)

# Library version (static library)
add_library(foo_lib STATIC ${FOO_SRCS})
target_link_libraries(foo_lib foo_subproject_1 foo_subproject_2)

Project Foo 的子目录大致具有以下架构:

cmake_minimum_required(VERSION 2.6)
project(foo_subproject_<n>)

set(FOO_SUBPROJECT_<N>_SRCS
    # source and headers for subproject
)

# foo_subproject's remote libraries are all static
add_library(some_lib STATIC IMPORTED)
set_target_properties(some_lib PROPERTIES IMPORTED_LOCATION /path/to/libsome_lib.a)

add_library(some_other_lib STATIC IMPORTED)
set_target_properties(some_other_lib PROPERTIES IMPORTED_LOCATION /path/to/libsome_other_lib.a)

include_directories(/paths/to/libs/include/)

# Static library for foo_subproject_N, links against static libs above
add_library(foo_subproject_<N> STATIC ${FOO_SUBPROJECT_<N>_SRCS})
target_link_libraries(foo_subproject_<N> some_library some_other_library)

项目栏是这样排列的:

cmake_minimum_required(VERSION 2.6)
project(bar)

set(BAR_SRCS
    # source and headers for main bar project
)

# Project Bar libraries are remote from Bar's perspective
add_library(foo_lib STATIC IMPORTED)
set_target_properties(foo_lib PROPERTIES IMPORTED_LOCATION /path/to/foo/libfoo_lib.a)

include_directories(/path/to/foo/include/)

# Runtime executable
add_executable(bar main.c ${BAR_SRCS} foo_lib)

Project Bar 无法链接(编译正常)并出现多个表单错误:

bar_frobulator.cpp:123: undefined reference to 'foo_subproject_1_init_frobulation'

foo_subproject_1_init_frobulation 位于 foo_subproject_1 中的位置

【问题讨论】:

  • 库的顺序可能很重要。如果 A 库需要 B 库中的项目,则 B 库应放在 A 库之后。
  • @john 我认为这不是问题所在。 Foo 的子项目以正确的顺序拥有它们的库,但子项目并不相互依赖,因此对 Foo 无关紧要。 Bar 的库也是独立的(为简洁起见,此处仅显示了 foo_lib,但没有一个库是相互连接的)
  • 不是你需要明确包含 foo 子项目库的答案吗?如果我正确阅读了上述内容,那么您只是在链接 foo 项目而不是子项目。
  • @john 也许(但如果属实,Bar 阶段最终会有很多东西要链接,所以如果可以的话,我宁愿避免这种情况) .如果为真,为什么 Foo 运行时有效?它不链接到 some_library (它的子项目这样做),但愉快地调用这些函数。为什么这对 Foo 有效,但对 Bar 无效?
  • Foo 项目中,您以一种令人困惑的方式使用add_executable:通过在add_executable 调用中列出foo_subproject_1,您(可能)期望链接,但是这种形式只暗示文件依赖,而不是链接。要链接可执行文件,请使用target_link_libraries(foo_runtime foo_subproject_1)add_library 调用也存在类似问题。在当前的表格中,我看不出FooBar 项目之间没有任何关系,我们可以比较一下:它们是完全不同的项目,它们链接不同的库。

标签: c++ cmake linker g++


【解决方案1】:

有人可以解释一下 Project Foo 是如何成功工作的,而 Project Bar(没有)按预期工作吗?

简而言之:创建 STATIC 库不涉及链接步骤!

详情

在 Foo 项目中,您有一个可执行文件 foo_runtime,它“有效”,因为它与适当的库链接(例如,与定义 foo_subproject_1_init_frobulation 符号的库 foo_subproject_1 链接)。

来自 Bar 项目的可执行文件 bar 不执行该链接,因此它失败了。线

target_link_libraries(bar foo_lib)

foo_lib 链接,但此库未定义所需的符号foo_subproject_1_init_frobulation

注意,那一行

target_link_libraries(foo_lib foo_subproject_1 foo_subproject_2)

在 Foo 项目中不执行实际链接:通常,构建静态库不涉及链接步骤。

只要 propagates 行包含从foo_subproject_* 库到foo_lib 的目录(和其他编译功能)。

如何让它工作

因为静态库foo_lib 不跟踪它的依赖关系,您需要将bar 与知道这一点的库链接。例如,将 foo_lib 共享,或将 foo_subproject_* 库合并到存档库中,如引用的问题 How to combine several C/C++ libraries into one? 所建议的那样。

或者,您可以在Bar 之一中构建Foo 子项目,而不是创建导入的foo_lib 目标,而是使用在Foo 项目中创建的“正常”foo_lib 目标。在这种情况下,行

target_link_libraries(bar foo_lib)

这意味着 CMake 将(实际上)链接 barfoo_subproject_* 库,因为这些库被“链接”(在 CMake 意义上)到 foo_lib。同样,最后一个“链接”仅对 CMake 有意义:文件 foo_lib.a 不知道需要 foo_subproject_* 库。

【讨论】:

  • 那么 foo_runtime 是怎么回事?如果target_link_libraries(foo_subproject_1 some_library some_other_library) 不做链接,那么为什么target_link_libraries(foo_runtime foo_subproject_1 foo_subproject_2) 工作?还是propegates include directories (and other compile-features)这一行的线索?
  • 构建静态库(foo_subproject_1)不涉及链接步骤,构建可执行文件(foo_runtime)涉及链接步骤。
  • @KidneyShris: foo_runtime 不是静态库,它是可执行文件,所以链接正常。在您的问题帖子中,您将其直接与子库链接,这些子库定义了所有需要的符号。但是您是否会将其与foo_lib 链接起来,然后 CMake 传播将占据一席之地,如我的答案末尾所述。
  • 啊哈!我觉得我明白了。查找 CMake 文档 - “库依赖项默认情况下具有此签名可传递。当此目标链接到另一个目标时,链接到此目标的库也将出现在另一个目标的链接行上。” - 这是否意味着在我的子项目中包含target_link_libraries 只会让主项目看到需要 some_library 和 some_other_library。 foo_runtime 进行链接,并获取依赖项。 foo_lib 不做链接,所以大部分是未定义的?
  • 是的,“传递库依赖项”是 CMake 文档中的正确位置。实际上,foo_lib 包含这些依赖项.. 但只要 CMake 目标 在 Foo 项目中。 文件 foo_lib.a,作为静态库,不包含依赖项。
【解决方案2】:

Tsyvarev 的回答很好地描述了我实际在做什么(而不是我认为我在做什么)让我查找正确的事情来回答我的根本问题(“为什么 foo_runtime 有效但 bar_runtime 不't,当两者都链接到静态库链接到静态库时?")

来自the CMake documentation for target_link_libraries

默认情况下,使用此签名的库依赖项是可传递的。当这个目标链接到另一个目标时,链接到这个目标的库也会出现在另一个目标的链接行上。

target_link_libraries 不会导致静态库 foo_subproject_1foo_subproject_2 中的链接(静态库不调用链接器)。所做的是使所需库的列表可用于任何尝试与 foo_subproject_1foo_subproject_2 链接的内容

因此,就 CMake 而言,我的 foo_runtimefoo_lib target_link_libraries 命令实际上是:

target_link_libraries(foo_runtime foo_subproject_1 some_library some_other_library foo_subproject_2 some_library some_other_library)
target_link_libraries(foo_lib foo_subproject_1 some_library some_other_library foo_subproject_2 some_library some_other_library)

foo_lib,是静态的,不会调用链接器foo_runtime,作为一个可执行文件,确实如此。

bar 是一个完全不同的项目,因此无法利用传递依赖项(并且链接失败,因为我缺少低级库中的所有符号)。

没有预料到 target_link_libraries 的这种行为,因为整个项目的行为有点误导(因为看起来我在子项目级别捆绑了一堆库,然后在上层捆绑了这些库. 实际上,我只是告诉上层它需要的所有库是什么)。

总结:

Q“为什么 foo_runtime 工作而 bar_runtime 不工作,当两者都链接到静态库链接到静态库时?”

A“foo_runtime 没有链接到静态库,静态库链接到静态库。foo_runtime 链接到的静态库比您最初认为的要链接的要多。

bar_runtime 不起作用,因为您的 foo_lib 基本上是空的"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 2012-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多