C++如何利用CMake的Object Libraries优化大型项目的编译依赖?(构建系统)

2次阅读

能,Object_library通过预编译共享对象文件减少重复编译,但仅在多目标共用相同源码且正确引用时有效;改头文件仍会触发整个object library重编译。

C++如何利用CMake的Object Libraries优化大型项目的编译依赖?(构建系统)

什么是 OBJECT_LIBRARY,它真能减少重编译吗?

能,但只在特定结构下有效——它把源码编译成中间对象文件(.o.obj),不打包成静态库,也不生成符号表,因此不会触发下游目标的全量重链接。关键在于:它让多个可执行目标或库共享同一组预编译对象,避免重复编译相同源码。

常见错误现象:add_library(myobj OBJECT src/a.cpp src/b.cpp) 写对了,但后续 target_sources(exe1 private $<myobj>)</myobj> 漏掉 PRIVATE 或路径拼错,导致对象没被实际引用,白忙一场。

使用场景:

  • 多个测试可执行文件共用同一套工具函数和 fixture 类(比如 test_utils.cpp + mock_network.cpp
  • 主程序和插件模块共享核心解析逻辑,但插件需独立加载
  • 跨平台项目中,把平台无关逻辑抽为 object library,再按需链接进 windows DLL / linux SO

target_sources() 引用 object library 时为什么总报 “unknown target”?

因为 $<xxx></xxx> 是生成器表达式,只能用在支持该语法的命令里,最典型的是 target_sources()target_link_libraries()(后者仅限 CMake 3.12+ 且仅用于链接时传递依赖,不推荐)。如果写在 add_executable() 的源码列表里,CMake 会直接报错说找不到 target。

立即学习C++免费学习笔记(深入)”;

正确做法:

  • 必须先定义 add_library(myobj OBJECT ...),且名字不能含 :: 或空格
  • 引用时严格用 target_sources(myexe PRIVATE $<myobj>)</myobj>,不能写成 target_sources(myexe PRIVATE myobj)
  • 如果 object library 本身依赖其他库(比如用了 fmt::fmt),得用 target_link_libraries(myobj PRIVATE fmt::fmt),否则下游链接时报 undefined reference

参数差异:PRIVATE 表示只影响当前目标,不会把 myobj 的头文件路径或编译定义透传给依赖它的其他目标——这点和静态库不同,得手动补 target_include_directories()

OBJECT_LIBRARY 后,修改头文件还会触发重编译吗?

会,而且可能比想象中更频繁。object library 本身不管理头文件依赖,CMake 只根据源文件时间戳决定是否重编译对象;一旦你改了被 myobj 中某个 .cpp 包含的头文件(比如 common.h),整个 myobj 所有对象都会重编译——哪怕只有 1 个 .cpp 实际用到了这个头文件。

性能影响:

  • 优势:避免多目标重复编译同一份 .cpp,尤其适合 CI 上并行构建多个测试二进制
  • 代价:失去细粒度增量编译能力;若 object library 包含 20 个源文件,改一个头文件就 recompile 20 个对象
  • 兼容性:CMake 3.12+ 才完全支持 $<...></...>

text=ZqhQzanResources