如何避免CMake打包静态C++库时出现源文件重复编译问题
问题根源
你当前配置重复编译的核心原因是:foo的target_sources使用了PUBLIC属性,这会将foo.cpp作为接口源文件传递给所有依赖foo的目标(也就是bar),导致foo.cpp既会在编译foo目标时编译一次,又会在编译bar目标时再编译一次。
如果改为PRIVATE,源文件不会向上传递,但静态库默认链接时只会提取被用到的符号,不会将整个libfoo.a合并到libbar.a中,因此foo.cpp.o不会出现在最终的libbar.a里。
解决方案1:使用OBJECT目标(兼容CMake 3.14+,推荐)
将foo定义为CMake的OBJECT类型目标,它只会编译一次生成对象文件,不会生成单独的静态库归档,你可以直接复用这些对象文件来构建libbar:
修改foo/CMakeLists.txt
cmake_minimum_required(VERSION 3.14) project(foo) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 定义为OBJECT目标,仅编译生成对象文件 add_library(foo OBJECT foo.cpp) # 如果你同时需要生成独立的libfoo.a,可额外添加下面这行 # add_library(foo_static STATIC $<TARGET_OBJECTS:foo>)
修改顶层CMakeLists.txt
cmake_minimum_required(VERSION 3.14) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) project(bar) add_subdirectory(foo) add_library(bar STATIC) # 直接复用foo目标编译好的对象文件,不会重复编译 target_sources(bar PRIVATE bar.cpp $<TARGET_OBJECTS:foo>)
该方案下foo.cpp仅编译一次,最终的libbar.a会同时包含bar.cpp.o和foo.cpp.o,完全符合需求。
解决方案2:使用WHOLE_ARCHIVE链接(适合需保留foo为独立静态库的场景,要求CMake 3.24+)
如果你需要保留foo作为独立的静态库目标,可以用CMake内置的跨平台全归档链接语法,避免源文件传递,同时将整个libfoo.a合并到libbar.a中:
修改foo/CMakeLists.txt
cmake_minimum_required(VERSION 3.14) project(foo) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(foo STATIC) # 源文件用PRIVATE,不传递给依赖目标 target_sources(foo PRIVATE foo.cpp)
修改顶层CMakeLists.txt
# 要求最低版本升级到3.24,支持LINK_LIBRARY生成器表达式 cmake_minimum_required(VERSION 3.24) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) project(bar) add_library(bar STATIC) add_subdirectory(foo) target_sources(bar PRIVATE bar.cpp) # 链接时完整包含foo静态库的所有内容 target_link_libraries(bar PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,foo>)
该方案下foo.cpp仅编译一次生成libfoo.a,链接libbar时会完整将libfoo.a的所有对象打包进libbar.a。
内容的提问来源于stack exchange,提问作者soad
相关产品推荐
相关产品推荐

