如何在CMake独立子目录中高效编译GoogleTest测试且避免重复编译?
CMake主程序与测试共享源码避免重复编译的解决方案
推荐方案:拆分主程序为静态库+轻量执行文件
这是软件工程中处理此类问题的标准做法,完全满足你“仅构建单个ProjectMain可执行文件”的需求,同时彻底消除重复编译:
根目录CMakeLists.txt配置:
cmake_minimum_required(VERSION 3.10) project(Project) # 配置全局包含目录 include_directories(${PROJECT_SOURCE_DIR}/include) # 创建静态库,封装所有业务逻辑代码(排除main.cpp) add_library(ProjectLib STATIC src/foo_class.cpp ) # 构建主程序可执行文件,仅依赖静态库和main函数文件 add_executable(ProjectMain src/main.cpp ) target_link_libraries(ProjectMain PRIVATE ProjectLib) # 引入测试子项目 add_subdirectory(test)
测试目录test/CMakeLists.txt配置:
# 查找并导入Google Test find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) include_directories(${PROJECT_SOURCE_DIR}/include) # 构建测试可执行文件 add_executable(ProjectTest src/foo_class_test.cpp ) # 链接静态库和gtest_main,复用已编译的业务代码 target_link_libraries(ProjectTest PRIVATE ProjectLib GTest::gtest_main ) # 自动发现并注册测试用例 include(GoogleTest) gtest_discover_tests(ProjectTest)
此方案中,foo_class.cpp仅编译一次(生成静态库),ProjectMain是独立的单个可执行文件,测试程序通过链接静态库直接获取业务代码的实现,没有重复编译开销,且代码结构更清晰、易维护。
替代方案:使用CMake对象库(Object Library)
如果确实不想生成独立的库文件,可以用CMake的对象库——一种仅编译源码为目标文件、不生成完整库的中间目标,同样实现源码单次编译:
根目录CMakeLists.txt调整:
cmake_minimum_required(VERSION 3.10) project(Project) include_directories(${PROJECT_SOURCE_DIR}/include) # 创建对象库,编译业务源码为目标文件 add_library(ProjectObjects OBJECT src/foo_class.cpp ) # 构建主程序,直接复用对象库的目标文件 add_executable(ProjectMain src/main.cpp $<TARGET_OBJECTS:ProjectObjects> ) add_subdirectory(test)
测试目录test/CMakeLists.txt调整:
find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) include_directories(${PROJECT_SOURCE_DIR}/include) # 构建测试程序,复用对象库的目标文件 add_executable(ProjectTest src/foo_class_test.cpp $<TARGET_OBJECTS:ProjectObjects> ) target_link_libraries(ProjectTest PRIVATE GTest::gtest_main) include(GoogleTest) gtest_discover_tests(ProjectTest)
对象库不会生成额外的库文件,直接复用编译好的目标文件,既保证主程序是单个可执行,又避免了重复编译。
为什么不推荐“直接将主程序源码添加到测试目标”
这种方式会触发重复编译,因为CMake会为每个可执行目标独立编译源码,导致相同的foo_class.cpp被编译两次,浪费构建资源,不建议采用。
内容的提问来源于stack exchange,提问作者master_clown
相关产品推荐
相关产品推荐

