CMake 嵌套的 OBJECT 库依赖项

CMake nested OBJECT library dependencies

我使用 OBJECT 库类型创建了一个包含一些嵌套库依赖项的项目。这样做的动机是为了避免 link 静态库的顺序问题。

我有一个简单的目录结构如下:

├── bar
│   ├── bar.c
│   └── bar.h
├── foo
│   ├── foo.c
│   └── foo.h
├── main.c
└── CMakeLists.txt

foo依赖于barmain依赖于foo。例如,

bar.c

#include "bar.h"

#include <stdio.h>

void bar(void) {
  printf("woot woot\n");
}

foo.c

#include "foo.h"

#include "bar.h"

void foo(void) {
  bar();
}

main.c

#include "foo.h"

int main() {
  foo();
  return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.18)

project(myproj)

enable_language(C ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

# build bar
add_library(bar OBJECT ${PROJECT_SOURCE_DIR}/bar/bar.c)
target_include_directories(bar PUBLIC ${PROJECT_SOURCE_DIR}/bar)

# build foo, depends on bar
add_library(foo OBJECT ${PROJECT_SOURCE_DIR}/foo/foo.c)
target_include_directories(foo PUBLIC ${PROJECT_SOURCE_DIR}/foo)
target_link_libraries(foo PUBLIC bar)

# build executable, depends on foo
add_executable(myexe ${PROJECT_SOURCE_DIR}/main.c)
target_link_libraries(myexe PUBLIC foo)

我的期望是 myexe 会在 link 继承 foobar 依赖项。然而,事实并非如此。编译代码时,main.o 仅 link 到 foo.o,因此存在对 bar 的未定义引用。但是,如果我将库类型从 OBJECT 更改为 STATIC 一切正常。为什么会这样?

对象库不能以这种方式链接。您必须 link 直接 (不传递)对象库以获取其对象文件。正如 the documentation

中所说

Object Libraries may "link" to other object libraries to get usage requirements, but since they do not have a link step nothing is done with their object files. [...] In other words, when Object Libraries appear in a target's INTERFACE_LINK_LIBRARIES property they will be treated as Interface Libraries, but when they appear in a target's LINK_LIBRARIES property their object files will be included in the link too.

不过,我同意这违背了所有理由。


但是,从 3.21 开始,您可以通过 target_link_libraries(INTERFACE)$<TARGET_OBJECTS> 生成器表达式手动传播目标文件(这样做在早期版本中存在错误,而 even-hacker target_sources 也可以在早期版本中使用。

请注意,这有点 hack,因为有些情况会中断。值得注意的是,如果库目标 link 公开指向此类目标,则目标文件将再次传播 。所以……小心点。

不过,这里是您的示例构建的更正版本:

cmake_minimum_required(VERSION 3.21)
project(myproj LANGUAGES C ASM)

set(CMAKE_C_STANDARD 11 CACHE STRING "The C standard to use")
option(CMAKE_C_STANDARD_REQUIRED "Enforce strict C standard selection" ON)
option(CMAKE_C_EXTENSIONS "Allow compiler-specific C extensions" OFF)

# build bar
add_library(bar OBJECT bar/bar.c)
target_include_directories(
  bar PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/bar>"
)

# build foo, depends on bar
add_library(foo OBJECT foo/foo.c)
target_include_directories(
  foo PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/foo>"
)
target_link_libraries(foo PUBLIC bar "$<TARGET_OBJECTS:bar>")

# build executable, depends on foo
add_executable(myexe main.c)
target_link_libraries(myexe PRIVATE foo)

在命令行:

$ cmake -G Ninja -S . -B build 
...
$ cmake --build build -- -nv
[1/4] /usr/bin/cc  -I/path/to/bar -std=c11 -MD -MT CMakeFiles/bar.dir/bar/bar.c.o -MF CMakeFiles/bar.dir/bar/bar.c.o.d -o CMakeFiles/bar.dir/bar/bar.c.o -c /path/to/bar/bar.c
[2/4] /usr/bin/cc  -I/path/to/foo -I/path/to/bar -std=c11 -MD -MT CMakeFiles/foo.dir/foo/foo.c.o -MF CMakeFiles/foo.dir/foo/foo.c.o.d -o CMakeFiles/foo.dir/foo/foo.c.o -c /path/to/foo/foo.c
[3/4] /usr/bin/cc  -I/path/to/foo -I/path/to/bar -std=c11 -MD -MT CMakeFiles/myexe.dir/main.c.o -MF CMakeFiles/myexe.dir/main.c.o.d -o CMakeFiles/myexe.dir/main.c.o -c /path/to/main.c
[4/4] : && /usr/bin/cc   CMakeFiles/foo.dir/foo/foo.c.o CMakeFiles/myexe.dir/main.c.o -o myexe  CMakeFiles/bar.dir/./bar/bar.c.o && :