静态库通过CMake与其他静态库链接-一种有效,一种无效。为什么?

时间:2018-08-27 11:36:13

标签: c++ cmake linker g++

背景 我有一个使用其他较小项目的项目。这些项目本身由其他项目组成。其中很多是遗留的,或者按原样安排还有其他管理上的原因,因此将所有内容都滚动到单个项目中是不可行的。有些库是在远程共享上预编译的。

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

  • Project Foo 是一个可执行文件和库,它链接了几个静态子项目(foo_subproject_1foo_subproject_n)。这些子项目还与远程位置(some_libsome_other_lib)的静态库链接。 Project Foo的可执行文件正确编译,链接并运行

  • 项目栏是一个可执行文件,可以链接几个其他项目,包括libFoo。链接失败,原因是“未定义对foo_subproject个函数的引用

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

gcc和g ++ 4.9.2是编译器(已经在C中包含某些部分,而在C ++中具有某些部分的外部“ C”问题)


问题

我对CMake add_sub目录的工作方式或链接器的工作方式误解了一个或两个。有人可以解释一下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失败的原因。在调查的这个阶段,我希望这两个命令都会失败。


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)

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中的地方

2 个答案:

答案 0 :(得分:3)

  

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

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

详细信息

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

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项目中的

不执行实际的链接:通常,构建静态库不涉及链接步骤。

仅将行传播包括从foo_subproject_*库到foo_lib的目录(以及其他编译功能)。

如何使其工作

由于静态库foo_lib无法跟踪其依赖关系,因此您需要将bar与已知的库链接。例如,根据参考问题How to combine several C/C++ libraries into one?的建议,共享foo_lib或将foo_subproject_*个库合并到存档库中。

或者,您可以在一个Foo内构建Bar子项目,而不是创建在{{}中创​​建的“正常” foo_lib目标,而不要创建导入的foo_lib目标。 1}}项目。在这种情况下,第

Foo

对于CMake而言,实际上意味着将target_link_libraries(bar foo_lib) bar库链接起来,因为这些库是从CMake的意义上“链接”到foo_subproject_*中的。同样,最后的“链接”仅对CMake有意义:文件foo_lib并不知道需要foo_lib.a库。

答案 1 :(得分:0)

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基本为空”