CMake中使用多个GTest测试用例出现链接错误,求解决方案
项目结构
- include - func.h - src - main.cpp - func.cpp - tests - CMakeLists.txt - func1_test.cpp - func2_test.cpp - CMakeLists.txt
相关配置与代码
根目录CMakeLists.txt
cmake_minimum_required (VERSION 3.12) option(BUILD_TESTS "BuildTests" ON) set(PROJECT_VERSION "0.0.3" CACHE INTERNAL "Version") project ("test_project" VERSION ${PROJECT_VERSION}) add_executable (test_project "src/main.cpp" "src/func.cpp" "include/func.h") include_directories("include") set_property(TARGET test_project PROPERTY CXX_STANDARD 20) # Enable Hot Reload for MSVC compilers if supported. if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>") endif() set_target_properties(test_project PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" ) # Tests. if(BUILD_TESTS) enable_testing() add_subdirectory(tests) endif()
测试目录CMakeLists.txt
include(FetchContent) if (POLICY CMP0135) cmake_policy(SET CMP0135 NEW) endif() FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) include(GoogleTest) set(TestsToRun func1_test.cpp func2_test.cpp ) create_test_sourcelist (Tests CppTests.cpp ${TestsToRun}) add_executable (CppTests ${Tests}) target_include_directories(CppTests PRIVATE "include") set_property(TARGET CppTests PROPERTY CXX_STANDARD 20) target_link_libraries(CppTests GTest::gtest_main) set_target_properties(CppTests PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" ) foreach (test ${TestsToRun}) get_filename_component (TName ${test} NAME_WE) add_test (NAME ${TName} COMMAND CppTests ${TName}) #gtest_discover_tests(${test}) endforeach ()
func.h
#pragma once int func1(); int func2();
func.cpp
#include "func.h" int func1() { return 33; } int func2() { return 77; }
func1_test.cpp(func2_test.cpp类似)
#include <sstream> #include <limits> #include <gtest/gtest.h> #include "../include/func.h" TEST(Func1Test, Func1Test) { ASSERT_EQ(func1(), 33); }
编译错误
[main] Building folder: /home/user/Code/Test/build/linux-debug [build] Starting build [proc] Executing command: /usr/bin/cmake --build /home/user/Code/Test/build/linux-debug --parallel 22 -- [build] [ 20%] Built target test_project [build] [ 33%] Built target gtest [build] [ 46%] Built target gtest_main [build] [ 60%] Built target gmock [build] [ 73%] Built target gmock_main [build] [ 80%] Linking CXX executable ../bin/CppTests [build] /usr/bin/ld: CMakeFiles/CppTests.dir/CppTests.cpp.o:(.data.rel.ro+0x8): undefined reference to `func1_test(int, char**)' [build] /usr/bin/ld: CMakeFiles/CppTests.dir/CppTests.cpp.o:(.data.rel.ro+0x18): undefined reference to `func2_test(int, char**)' [build] /usr/bin/ld: CMakeFiles/CppTests.dir/func1_test.cpp.o: in function `Func1Test_Func1Test_Test::TestBody()': [build] /home/user/Code/Test/tests/func1_test.cpp:9:(.text+0x28): undefined reference to `func1()' [build] /usr/bin/ld: CMakeFiles/CppTests.dir/func2_test.cpp.o: in function `Func21Test_Func2Test_Test::TestBody()': [build] /home/user/Code/Test/tests/func2_test.cpp:(.text+0x28): undefined reference to `func2()' [build] collect2: error: ld returned 1 exit status [build] gmake[2]: *** [tests/CMakeFiles/CppTests.dir/build.make:131: bin/CppTests] Error 1 [build] gmake[1]: *** [CMakeFiles/Makefile2:180: tests/CMakeFiles/CppTests.dir/all] Error 2 [build] gmake: *** [Makefile:146: all] Error 2 [proc] The command: /usr/bin/cmake --build /home/user/Code/Test/build/linux-debug --parallel 22 -- exited with code: 2 [driver] Build completed: 00:00:00.352 [build] Build finished with exit code 2
问题分析与解决
1. 未链接核心函数实现
错误中的undefined reference to func1()/func2(),是因为测试程序CppTests只包含测试代码,未关联src/func.cpp的编译结果,导致链接时找不到函数实现。
解决方法:将核心代码封装为库
修改根目录CMakeLists.txt,把核心代码做成静态库,主程序和测试程序都链接这个库:
cmake_minimum_required (VERSION 3.12) option(BUILD_TESTS "BuildTests" ON) set(PROJECT_VERSION "0.0.3" CACHE INTERNAL "Version") project ("test_project" VERSION ${PROJECT_VERSION}) # 核心代码封装为静态库 add_library(core_lib STATIC "src/func.cpp" "include/func.h") target_include_directories(core_lib PUBLIC "include") set_property(TARGET core_lib PROPERTY CXX_STANDARD 20) # 主程序链接核心库 add_executable(test_project "src/main.cpp") target_link_libraries(test_project PRIVATE core_lib) set_property(TARGET test_project PROPERTY CXX_STANDARD 20) # ... 其余原有配置不变 ... # Tests. if(BUILD_TESTS) enable_testing() add_subdirectory(tests) endif()
然后在测试目录CMakeLists.txt中,让CppTests链接核心库:
# ... FetchContent部分不变 ... include(GoogleTest) # 直接添加测试文件,无需create_test_sourcelist add_executable(CppTests func1_test.cpp func2_test.cpp) target_include_directories(CppTests PRIVATE "../include") set_property(TARGET CppTests PROPERTY CXX_STANDARD 20) # 链接核心库与GTest target_link_libraries(CppTests PRIVATE core_lib GTest::gtest_main) # ... 输出目录配置不变 ... # 自动发现测试用例 gtest_discover_tests(CppTests)
2. 错误使用create_test_sourcelist
create_test_sourcelist是CMake为传统非GTest测试框架设计的工具,要求每个测试文件必须定义与文件名同名的函数(比如func1_test.cpp中需写int func1_test(int argc, char** argv)),但GTest的测试用例是通过TEST宏自动生成的,不符合该要求,因此出现undefined reference to func1_test(int, char**)错误。
解决方法:完全移除create_test_sourcelist相关代码,直接将测试文件添加到可执行目标,使用GTest原生方式管理测试用例。
gtest_discover_tests的正确用法
gtest_discover_tests是CMake集成GTest的推荐方式,它会自动运行测试可执行文件,扫描所有用TEST/TEST_F定义的测试用例,并为每个用例生成对应的CTest测试项,无需手动编写add_test循环。
使用规则:
- 必须先创建测试可执行目标(如
CppTests) - 调用
gtest_discover_tests(目标名),替代所有手动add_test逻辑 - 调用前需先执行
include(GoogleTest)
修改后的测试目录CMakeLists核心部分:
# ... FetchContent部分不变 ... include(GoogleTest) add_executable(CppTests func1_test.cpp func2_test.cpp) target_include_directories(CppTests PRIVATE "../include") set_property(TARGET CppTests PROPERTY CXX_STANDARD 20) target_link_libraries(CppTests PRIVATE core_lib GTest::gtest_main) set_target_properties(CppTests PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" ) gtest_discover_tests(CppTests)
修改后即可解决所有链接错误,同时CTest会自动发现并管理所有GTest测试用例。
内容的提问来源于stack exchange,提问作者qloq

