构建C++静态库时,源文件中第三方库为何成为用户依赖?
问题:静态库依赖SDL导致引用者需额外链接SDL的原因
错误信息
编译引用tst_engine.lib的可执行项目时,出现以下链接错误:
lld-link: error: undefined symbol: SDL_Init >>> referenced by C:\Users\n10255460\CLionProjects\2D_engine_and_game\tst_engine\TstGameEngine.cpp:22 >>> tst_engine.lib(TstGameEngine.cpp.obj):(public: __cdecl tst::TstGameEngine::TstGameEngine(bool *)) lld-link: error: undefined symbol: SDL_Quit >>> referenced by C:\Users\n10255460\CLionProjects\2D_engine_and_game\tst_engine\TstGameEngine.cpp:43 >>> tst_engine.lib(TstGameEngine.cpp.obj):(public: __cdecl tst::TstGameEngine::~TstGameEngine(void)) ninja: build stopped: subcommand failed.
背景情况
- SDL仅在
tst_engine的.cpp文件中使用,未出现在头文件内 - 若将SDL作为依赖添加到引用
tst_engine.lib的可执行项目,错误会消失 - 原本认知:如果第三方库仅在静态库的源文件中使用,静态库的使用者无需额外链接该第三方库
相关CMake配置
触发错误的可执行项目配置
cmake_minimum_required(VERSION 3.24) project(game) set(CMAKE_CXX_STANDARD 20) include_directories(../tst_engine) set(lib ${CMAKE_SOURCE_DIR}\\libraries\\lib) include_directories(${CMAKE_SOURCE_DIR}\\libraries\\include) add_executable(game main.cpp) target_link_libraries(game ${CMAKE_SOURCE_DIR}\\cmake-build-debug\\tst_engine\\tst_engine.lib )
编译正常的静态库项目配置
cmake_minimum_required(VERSION 3.24) project(tst_engine) set(CMAKE_CXX_STANDARD 20) set(lib ${CMAKE_SOURCE_DIR}\\libraries\\lib) include_directories(${CMAKE_SOURCE_DIR}\\libraries\\include) add_library(tst_engine STATIC TstGameEngine.h TstGameEngine.cpp) target_link_libraries(tst_engine PRIVATE ${lib}\\SDL2main.lib ${lib}\\SDL2.lib ${lib}\\SDL2-static.lib)
疑问
为什么会出现这种情况?
解答
核心原因是静态库不会自动合并它依赖的其他静态库代码。
当你用PRIVATE将SDL链接到tst_engine静态库时,CMake仅会在编译tst_engine的目标文件时识别SDL的存在,但不会把SDL的代码打包进tst_engine.lib。静态库本质是一堆目标文件的归档,只保留自身代码对外部符号的引用,不会解析并包含依赖库的实现。
链接可执行文件game时,链接器需要找到所有被引用的符号(比如SDL_Init和SDL_Quit),这些符号仅存在于SDL的库文件中,tst_engine.lib里只有对它们的引用,没有具体实现。因此必须将SDL的库也链接到可执行文件,链接器才能找到这些缺失的符号。
如果想让tst_engine的使用者无需手动链接SDL,有两种解决办法:
- 将
tst_engine改为动态库(SHARED):动态库编译时可将依赖库链接进去(或运行时加载),使用者仅需链接tst_engine即可。 - 修改
tst_engine的CMake配置,将SDL的链接属性从PRIVATE改为PUBLIC或INTERFACE:这样CMake会自动将SDL的依赖传递给所有链接tst_engine的目标,使用者无需手动添加SDL链接。
修改后的静态库CMake配置示例:
target_link_libraries(tst_engine PUBLIC ${lib}\\SDL2main.lib ${lib}\\SDL2.lib ${lib}\\SDL2-static.lib)
这样链接game时,CMake会自动将SDL的库加入链接列表,不会再出现符号未定义的错误。
内容的提问来源于stack exchange,提问作者Jack Benson
相关产品推荐
相关产品推荐

