如何在 windows 中使用 cmake link SDL_ttf 和 SDL_image 库?

How to link SDL_ttf and SDL_image libraries using cmake in windows?

我无法将 SDL_ttfSDL_image 包括并链接到我的项目。我有一个仅适用于 Clion 上的 SDLSDL_gfx 的 cmake 文件。我想问题出在 cmake 文件中。 我在构建项目时遇到了几个错误: undefined reference to `FUNCTION'

我在项目中使用的库:https://github.com/satayyeb/sdl2-libraries

sdl2文件夹(库文件所在)树:

sdl2-|
     |-sdl2-image-include
     |    |-SDL_image.h
     |
     |-sdl2-image-lib
     |    |-libSDL2_image.a
     |    |-libSDL2_image.dll.a
     |    |-libSDL2_image.la
     |
     |-sdl2-ttf-include
     |    |-SDL_ttf.h
     |
     |-sdl2-ttf-lib
          |-libSDL2_ttf.a
          |-libSDL2_ttf.dll.a
          |-libSDL2_ttf.la

CMakeLists.txt :

set(SOURCE src/main.c)
set(PROJECT_NAME state.SAT)

cmake_minimum_required(VERSION 3.19)

project(${PROJECT_NAME} C)

set(CMAKE_C_STANDARD 11)

set(SDL2_INCLUDE_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-include")
set(SDL2_LIB_DIR         "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-lib")

set(SDL2_GFX_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-include")
set(SDL2_GFX_LIB_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-lib")

set(SDL2_IMAGE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-include")
set(SDL2_IMAGE_LIB_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-lib")

set(SDL2_TTF_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-include")
set(SDL2_TTF_LIB_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-lib")

set(SDL2_FLAGS           "-mwindows -Wl,--no-undefined -static-libgcc")


add_library(SDL2       STATIC IMPORTED)
add_library(SDL2main   STATIC IMPORTED)
add_library(SDL2_GFX   STATIC IMPORTED)
add_library(SDL2_IMAGE STATIC IMPORTED)

add_library(SDL2_TTF   STATIC IMPORTED)

set_property(TARGET SDL2       PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2.a")
set_property(TARGET SDL2main   PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2main.a")
set_property(TARGET SDL2_GFX   PROPERTY IMPORTED_LOCATION "${SDL2_GFX_LIB_DIR}/libsdl-gfx.a")
set_property(TARGET SDL2_IMAGE PROPERTY IMPORTED_LOCATION "${SDL2_IMAGE_LIB_DIR}/libSDL2_image.a")
set_property(TARGET SDL2_TTF   PROPERTY IMPORTED_LOCATION "${SDL2_TTF_LIB_DIR}/libSDL2_ttf.a")


set(SDL2_LIBS SDL2 SDL2main SDL2_GFX SDL2_TTF SDL2_IMAGE m dinput8 dxguid dxerr8 user32 gdi32 winmm imm32 ole32 oleaut32 shell32 version uuid)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${SDL2_FLAGS}")

file(GLOB_RECURSE SOURCE "src/*.c" "src/*.h")
add_executable("${PROJECT_NAME}" "${SOURCE}")

include_directories(${SDL2_INCLUDE_DIR} ${SDL2_GFX_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBS})

我发现了问题:

  1. sdl2-image-libsdl2-ttf-lib 目录中有 libSDL2_image.dll.alibSDL2_ttf.dll.a 必须链接的二进制文件而不是 libSDL2_image.alibSDL2_ttf.a 个文件。

  2. dll文件一定要拷贝到EXE文件所在的目录下。 dll 文件在库的 bin 文件夹中。

我在项目中使用的库: https://github.com/satayyeb/sdl2-libraries

和修改后的CMakeLists.txt:

set(SOURCE src/main.c)
set(PROJECT_NAME island_soldier)
cmake_minimum_required(VERSION 3.19)
project(${PROJECT_NAME} C)
set(CMAKE_C_STANDARD 11)


set(SDL2_INCLUDE_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-include")
set(SDL2_LIB_DIR         "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-lib")

set(SDL2_GFX_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-include")
set(SDL2_GFX_LIB_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-lib")

set(SDL2_IMAGE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-include")
set(SDL2_IMAGE_LIB_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-lib")

set(SDL2_TTF_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-include")
set(SDL2_TTF_LIB_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-lib")


set(SDL2_FLAGS   "-mwindows -Wl,--no-undefined -static-libgcc")


add_library(SDL2            STATIC IMPORTED)
add_library(SDL2main        STATIC IMPORTED)

add_library(SDL2_GFX        STATIC IMPORTED)

add_library(SDL2_IMAGE_DLL  STATIC IMPORTED)

add_library(SDL2_TTF_DLL    STATIC IMPORTED)



set_property(TARGET SDL2            PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2.a")
set_property(TARGET SDL2main        PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2main.a")

set_property(TARGET SDL2_GFX        PROPERTY IMPORTED_LOCATION "${SDL2_GFX_LIB_DIR}/libsdl-gfx.a")

set_property(TARGET SDL2_IMAGE_DLL  PROPERTY IMPORTED_LOCATION "${SDL2_IMAGE_LIB_DIR}/libSDL2_image.dll.a")

set_property(TARGET SDL2_TTF_DLL    PROPERTY IMPORTED_LOCATION "${SDL2_TTF_LIB_DIR}/libSDL2_ttf.dll.a")



set(SDL2_LIBS mingw32 SDL2 SDL2main SDL2_GFX SDL2_TTF_DLL SDL2_IMAGE_DLL m dinput8 dxguid dxerr8 user32 gdi32 winmm imm32 ole32 oleaut32 shell32 version uuid)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${SDL2_FLAGS}")
file(GLOB_RECURSE SOURCE "src/*.c" "src/*.h")


IF(WIN32)
    SET(GUI_TYPE WIN32)
ENDIF(WIN32)
IF (APPLE)
    SET(GUI_TYPE MACOSX_BUNDLE)
ENDIF (APPLE)

add_executable("${PROJECT_NAME}" ${GUI_TYPE} "${SOURCE}")

include_directories(${SDL2_INCLUDE_DIR} ${SDL2_GFX_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBS})

简短的回答是您使用的库不兼容,因为 SDL2_ttf 和 SDL2_image 是针对比您存档中的更新版本的 SDL2 构建的(linked 有问题)。获取较新的 SDL2,或较旧的 SDL2_ttf 和 SDL2_image,然后您无需更改任何内容。 更正 在写了冗长的解释之后,我注意到在您的存档中,即使是同一个库的静态和动态版本也有不同的版本;我建议从 SDL 网站重新下载全部内容,因为你拥有的是一团糟。

请注意,我的回答并未涵盖您的 CMakeLists.txt。你拥有的很windows-specific;如果您不关心其他系统 - 随心所欲。您的程序 可以 使用您在原始问题中认为的 CMakeLists 构建,这不是您问题的根源。

首先是否要使用静态库值得怀疑。如果没有充分的理由专门使用静态库,我建议您切换到动态库,即 linking 和 .dll.a(或者直接 .dll,mingw 工具链支持)。这样你就不需要长长的 dinput8 dxguid dxerr8 user32 gdi32 winmm imm32 ole32 oleaut32 shell32 version uuid 列表,如果你不知道的话,它们是你使用的 SDL2 版本的依赖项。意思是如果你更新你的静态 libSDL2.a,这个列表可能会有所不同,因为它现在依赖于更多的东西(这不是理论上的 - 最新的 SDL 2.0.20 实际上有更长的依赖列表)。

库不兼容的解释(很长,技术性很强,而且大部分 mingw/gcc 具体;如果您对这些细节不感兴趣,请查看末尾的最后注释):

您在问题中提到了undefined reference to 'FUNCTION',但其中FUNCTION非常重要;不要在你以后的问题中遗漏这些信息,如果你有的话。值得庆幸的是,您后来提供了 link 到完整的库存档,我们可以自己看到该构建尝试的结果,即:

../sdl2/sdl2-ttf-lib/libSDL2_ttf.a(libSDL2_ttf_la-SDL_ttf.o): In function `RWread':
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1583: undefined reference to `SDL_RWseek'
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1587: undefined reference to `SDL_RWread'
../sdl2/sdl2-ttf-lib/libSDL2_ttf.a(libSDL2_ttf_la-SDL_ttf.o): In function `TTF_CloseFont':
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:2544: undefined reference to `SDL_RWclose'
../sdl2/sdl2-ttf-lib/libSDL2_ttf.a(libSDL2_ttf_la-SDL_ttf.o): In function `TTF_OpenFontIndexDPIRW':
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1614: undefined reference to `SDL_RWtell'
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1647: undefined reference to `SDL_RWsize'
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1603: undefined reference to `SDL_RWclose'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__vecang':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2096: undefined reference to `SDL_acosf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__addActive':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:873: undefined reference to `SDL_floorf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:874: undefined reference to `SDL_floorf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:871: undefined reference to `SDL_floorf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__parseStrokeDashArray':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:1722: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__parseRect':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2364: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__parseCircle':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2413: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__pathArcTo':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2112: undefined reference to `SDL_fabsf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2113: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o):/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2115: more undefined references to `SDL_fabsf' follow
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__curveDivs':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:598: undefined reference to `SDL_acosf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:599: undefined reference to `SDL_ceilf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__roundJoin':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:548: undefined reference to `SDL_atan2f'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:549: undefined reference to `SDL_atan2f'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:556: undefined reference to `SDL_ceilf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__flattenShapeStroke':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:786: undefined reference to `SDL_fmodf'

好的,实际上不是很长的列表。那么什么是未定义的?很少 SDL_RW* 函数和很少的 SDL 数学函数。请注意,那是 而不是 唯一的 SDL 函数 SDL2_ttf 和 SDL2_image 使用,其他的都可以找到。为什么在您的构建中找不到该函数?..链接器说没有具有该名称的函数,是吗?由于您使用 mingw 工具链,因此您应该可以访问更多工具,而不仅仅是编译器,例如nm 工具(在控制台中):

> nm -a sdl2/sdl-libs/libSDL2.a | grep SDL_RWseek
>

(如果没有 grep,请使用 findstr)是的,没有。好吧,我们有 SDL2 源代码,包括版本历史,让我们看看为什么会这样。

让我们从例如SDL_RWseek。 SDL2 中有一个具有该名称的函数,我们可以在 https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_rwops.h#L401 中查看它(只是在 github 上搜索了函数名称并打开了一个头文件)。该函数之前的注释有一个答案:Prior to SDL 2.0.10, this function was a macro. ... This function is available since SDL 2.0.10.。哦。所以无论你有什么,似乎都低于 2.0.10,并且 SDL2_ttf 显然是为更新的东西构建的,而这已经是一个功能。

好的,SDL2_image 使用的数学函数呢?嗯,github 再次搜索指向 https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_stdinc.h#L572 ,好像说是2.0.2添加的;不幸的是,评论只涵盖 SDL_acos 功能,其他人未被评论。但是,如果您将 github 视图切换到“blame”(顺便说一下,这并不是 github 特有的)以查看每行最后一次修改的时间,您可以看到很多数学函数是添加了不同的提交,并跨越多个 。什么没找到? SDL_fmodf - 4 年前添加,第一个版本是 2.0.8。对于所有其他“未定义”函数也是如此。

因此,这些库需要 SDL2 2.0.10 和 2.0.8。你有什么版本?我没有看到一个快速的方法来确定,但是有一个 SDL_GetVersion 函数,为什么不制作调用它的最小程序:

#include "SDL.h"
#include <stdio.h>

int main(int argc, char **argv) {
    SDL_version v = {};
    SDL_GetVersion(&v);
    printf("%d.%d.%d\n", v.major, v.minor, v.patch);
    return 0;
}

构建&执行(重要的部分是它必须在您拥有的相同环境中构建!因此,出于所有意图和目的,这是您的 main.c),

的内容
2.0.7

哦。所以是的,它无法使用那个版本,没有它想调用的函数。

在您自己的回答中,您说您是通过将 link 添加到 libSDL2_ttf.dll.alibSDL2_image.dll.a 来构建它的,大概是由于图书馆的顺序。首先,库顺序只对动态库有影响(甚至动态库 - 不是每个 linker 实现),所以不,不是这样。但是为什么要建呢?我们将在这里 windows 特定领域,其概念是“导入库”; .dll.a 正是这样,一个包含带有“稍后将在运行时加载”符号的存根。它所做的是使您的结果程序取决于例如SDL2_ttf.dll。动态库与可执行文件非常相似,实际上有自己的依赖列表(静态库没有 - 这就是为什么你的 cmakelists 中有长长的 SDL2 依赖列表),所以 SDL2_ttf.dll 中有一个条目说它依赖在 SDL2.dll 上。再一次,您可以使用可信赖的编译器工具链进行检查:

> ldd sdl2/sdl2-ttf-bin/SDL2_ttf.dll | grep SDL2
        SDL2.dll => /c/WINDOWS/SYSTEM32/SDL2.dll (0x5ccb0000)

什么意思取决于SDL2.dll,如果我在当前环境中启动它,它将使用我在默认 dll 搜索路径中的 C:\Windows\system32\SDL2.dll(您的环境可能不同)。如果您使用相同的 ldd 工具检查生成的 exe,您将看到您现在依赖于 SDL2_ttf.dll,但因此 - 也依赖于 SDL2.dll。所以现在你已经在你的程序中静态 linked SDL2,但由于依赖关系也拉动态 SDL2.dll。所以它构建,甚至可能运行,但很难想象它会按预期工作。我敢肯定,当您尝试实际进行纹理或字体处理时,一切都会崩溃。

写完所有内容后我发现您的 SDL2.dll 实际上是 2.0.20,而 libSDL2.a 是 2.0.7。也许最好的做法是核对整个库存档并从 SDL 网站重新下载所有内容。