编译GTest单元测试时出现undefined reference错误,求排查解决
我有一个项目的目录结构如下:
根目录的CMakeLists.txt内容:
cmake_minimum_required(VERSION 3.0.0) project(yx_ip C) set(CMAKE_C_STANDARD 99) ######### # GTest # ######### enable_testing() add_subdirectory(libs/gtest-1.7.0) include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) ########### # Project # ########### add_executable(yx_ip main.c) add_subdirectory(libs/arp) add_subdirectory(libs/base) add_subdirectory(unit_test) target_link_libraries(yx_ip BASE ARP)
unit_test目录下的CMakeLists.txt内容:
cmake_minimum_required(VERSION 3.0.0) set(TEST_PROJECT_NAME UnitsTests) file(GLOB LIBRARY_TESTS_SOURCE *.cpp) project(${TEST_PROJECT_NAME}) enable_testing() if (NOT (TARGET gtest AND TARGET gtest_main)) message(FATAL_ERROR "gtest and gtest_main targets NOT found") endif() add_executable(${TEST_PROJECT_NAME} ${LIBRARY_TESTS_SOURCE}) #target_link_libraries(${TEST_PROJECT_NAME} BASE) target_link_libraries(${TEST_PROJECT_NAME} BASE gtest gtest_main) add_test(${TEST_PROJECT_NAME}, ${TEST_PROJECT_NAME})
执行make编译时出现错误:
unit_test/utils_test.cpp:6: undefined reference to `pasrse_ip_address(char*)' collect2: error: ld returned 1 exit status
但单独编译main.c时一切正常,请问该如何解决此问题?
这个问题大概率和C/C++混合编译的符号差异或者库依赖配置有关,我给你一步步拆解排查:
1. 先检查函数名拼写
先看错误里的函数名pasrse_ip_address——是不是少写了一个e?正确的应该是parse_ip_address?如果是拼写失误,直接修正测试代码里的函数名就能解决问题。要是确认拼写没问题,继续往下看。
2. 处理C/C++混合编译的名称修饰问题
你的主项目是C语言(根目录project(yx_ip C),源码是.c文件),但单元测试是C代码(.cpp文件)。C编译器会对函数名做名称修饰(Name Mangling),而C编译器不会,这就导致C++代码找不到C编译出来的函数符号。
解决方法是在C语言的头文件中添加extern "C"声明,让C++编译器按照C的规则解析这些函数:
假设pasrse_ip_address定义在libs/base的某个C文件里,对应的头文件(比如base_utils.h)要改成这样:
#ifndef BASE_UTILS_H #define BASE_UTILS_H #ifdef __cplusplus extern "C" { #endif // 声明你的函数 void pasrse_ip_address(char*); // 其他C函数声明... #ifdef __cplusplus } #endif #endif // BASE_UTILS_H
然后在你的C测试文件utils_test.cpp中包含这个头文件,这样C编译器就会用C的符号规则去查找函数。
3. 确认BASE库包含了目标函数
检查libs/base目录下的CMakeLists.txt,确保包含pasrse_ip_address定义的C文件被加入到库构建中。比如:
add_library(BASE STATIC base_utils.c # 假设这个文件里有pasrse_ip_address的实现 # 其他源文件... )
如果这个C文件没被加到BASE库,链接时自然找不到函数符号。
4. 优化库的头文件导出配置
为了让unit_test能正确找到BASE库的头文件,建议在libs/base/CMakeLists.txt中添加:
target_include_directories(BASE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
这样CMake会自动把BASE的头文件目录传递给依赖它的目标(比如你的单元测试),不用手动写include_directories。
5. 清理缓存重新构建
有时候CMake的缓存会导致奇怪的依赖问题,执行以下命令清理后重新构建试试:
rm -rf build/ # 如果用了build目录,没有的话跳过这步 mkdir build && cd build cmake .. make
按照上面的步骤排查,基本就能解决这个未定义引用的问题了。
内容的提问来源于stack exchange,提问作者Yann

