You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CMake构建单元测试时链接静态库出现未定义引用错误

C++项目GoogleTest单元测试链接错误与优化方案

问题场景

项目文件夹结构:

*--build
|---(building cmake here)
|
*--main.cpp
|
*--CMakeLists.txt (root)
|
*--modules
|---foo
*------foo.h
|------foo.cpp
|------CMakeLists.txt
*------test
|--------main.cpp
|--------CMakeLists.txt

根目录CMakeLists.txt:

cmake_minimum_required(VERSION 3.15.2)

project(foobar)
add_executable(${PROJECT_NAME} main.cpp)
add_library(foolib STATIC)

# Build sub-modules
include_directories(modules/foo)
add_subdirectory(modules/foo)

target_link_libraries(${PROJECT_NAME} PRIVATE foolib)
target_link_libraries(foo_test PRIVATE gtest_main foolib)

foo模块CMakeLists.txt:

file(GLOB FOO_SRC *.h *.cpp)

target_include_directories(foolib PUBLIC 
        ${CMAKE_CURRENT_SOURCE_DIR})

# add sources to the library
target_sources(foolib PUBLIC ${FOO_SRC})

# bootstrap gtest
if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(test)
endif()

测试目录CMakeLists.txt:

file(GLOB TEST_SRCS *.h *.cpp)

target_include_directories(foolib PUBLIC 
        ${CMAKE_CURRENT_SOURCE_DIR})

# add sources to the library
target_sources(foolib PUBLIC ${FOO_SRC})

include_directories(googletest)
include_directories(
    ${CMAKE_SOURCE_DIR}/modules/foo
    )

add_executable(foo_test ${TEST_SRCS})

gtest_discover_tests(foo_test)

测试代码main.cpp:

#include <iostream>
#include <string>

#include "../foo.h"

TEST(builder_tests, initial_test)
{
  std::string s = "please work";
  Foobar fb(s);
  EXPECT_EQ(fb.test_string, "please work");
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS();
}

构建测试时出现链接错误:

Linking CXX executable FOO_TEST
/usr/bin/ld: CMakeFiles/FOO_TEST.dir/main.cpp.o: in function `foo_tests_initial_test_Test::TestBody()':
/inference/modules/foo/test/main.cpp:15: undefined reference to `foo::foo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
collect2: error: ld returned 1 exit status
make[3]: *** [modules/foo/test/CMakeFiles/foo_TEST.dir/build.make:234: modules/foo/test/FOO_TEST] Error 1
make[2]: *** [CMakeFiles/Makefile2:842: modules/foo/test/CMakeFiles/foo_TEST.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:849: modules/foo/test/CMakeFiles/foo_TEST.dir/rule] Error 2
make: *** [Makefile:383: FOO_TEST] Error 2

错误原因分析

  1. 测试目录CMake配置错误:
    • 重复给foolib添加源文件,但FOO_SRC变量仅在foo模块CMake中定义,测试目录未声明,导致foolib未包含foo.cpp实现,链接时找不到构造函数。
    • 仅通过include_directories添加GoogleTest路径,但未实际链接库或拉取依赖源码。
  2. 根目录CMake顺序问题:
    • 提前调用target_link_libraries(foo_test ...),但foo_test是在BUILD_TESTS条件下才创建,此时目标尚未定义,引发配置异常。
  3. file(GLOB)的缺陷:
    • 依赖自动扫描源文件,新增文件不会触发CMake重新配置,可能导致源文件漏加。

修复步骤

1. 修正测试目录CMakeLists.txt

移除对foolib的重复配置,正确引入并链接GoogleTest:

file(GLOB TEST_SRCS *.h *.cpp)

add_executable(foo_test ${TEST_SRCS})

# 自动拉取GoogleTest依赖
include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

# 链接测试依赖库
target_link_libraries(foo_test PRIVATE gtest_main foolib)

gtest_discover_tests(foo_test)

2. 修正根目录CMakeLists.txt

添加测试开关,移除提前引用foo_test的逻辑:

cmake_minimum_required(VERSION 3.15.2)

project(foobar)
option(BUILD_TESTS "Build unit tests" ON)

add_executable(${PROJECT_NAME} main.cpp)
add_library(foolib STATIC)

add_subdirectory(modules/foo)

target_link_libraries(${PROJECT_NAME} PRIVATE foolib)

3. 修正foo模块CMakeLists.txt

显式列出源文件,替代file(GLOB):

set(FOO_SRC
    foo.h
    foo.cpp
)

target_include_directories(foolib PUBLIC 
        ${CMAKE_CURRENT_SOURCE_DIR})

target_sources(foolib PUBLIC ${FOO_SRC})

if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(test)
endif()

4. 修正测试代码的头文件引用

利用foolib的PUBLIC包含路径,直接引用头文件:

#include <iostream>
#include <string>

#include "foo.h"

TEST(builder_tests, initial_test)
{
  std::string s = "please work";
  Foobar fb(s);
  EXPECT_EQ(fb.test_string, "please work");
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS();
}

项目结构与CMake优化建议

  • 目录结构优化:
    • 将测试文件重命名为foo_test.cpp,明确对应模块;若后续新增多模块测试,可在根目录新增tests目录统一管理。
  • CMake最佳实践:
    • 完全替代include_directories,使用target_include_directories实现依赖隔离。
    • 永远显式列出源文件,避免file(GLOB)导致的文件遗漏问题。
    • 用FetchContent管理第三方依赖,保证开发环境一致性。
    • 测试目标的链接逻辑放在对应测试目录的CMake中,避免跨目录提前引用未定义目标。
  • 测试代码优化:
    • 避免直接访问类私有成员,提供公共接口获取值,符合封装原则。
    • 拆分测试用例,每个用例只验证单一场景。

内容的提问来源于stack exchange,提问作者Grudy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 23:01:23