CMake 嵌套的 OBJECT 库依赖项
CMake nested OBJECT library dependencies
我使用 OBJECT
库类型创建了一个包含一些嵌套库依赖项的项目。这样做的动机是为了避免 link 静态库的顺序问题。
我有一个简单的目录结构如下:
├── bar
│ ├── bar.c
│ └── bar.h
├── foo
│ ├── foo.c
│ └── foo.h
├── main.c
└── CMakeLists.txt
foo
依赖于bar
,main
依赖于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 继承 foo
的 bar
依赖项。然而,事实并非如此。编译代码时,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 && :
我使用 OBJECT
库类型创建了一个包含一些嵌套库依赖项的项目。这样做的动机是为了避免 link 静态库的顺序问题。
我有一个简单的目录结构如下:
├── bar
│ ├── bar.c
│ └── bar.h
├── foo
│ ├── foo.c
│ └── foo.h
├── main.c
└── CMakeLists.txt
foo
依赖于bar
,main
依赖于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 继承 foo
的 bar
依赖项。然而,事实并非如此。编译代码时,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'sLINK_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 && :