简体   繁体   中英

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

I have trouble including and linking SDL_ttf and SDL_image to my project. I have a cmake file that works only for SDL and SDL_gfx on Clion. I guess the problem is from the cmake file. I got several errors when I build the project: undefined reference to `FUNCTION'

The libraries which I used for my project: https://github.com/satayyeb/sdl2-libraries

sdl2 folder (where library files are located) tree:

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})

Short answer is libraries you use are incompatible, as SDL2_ttf and SDL2_image are built against newer version of SDL2 than you have in your archive (linked in question). Get newer SDL2, or older SDL2_ttf and SDL2_image, then you don't have to change anything. Correction after writing lengty explaination i noted that in your archive even static and dynamic versions of the same library have different versions; i would suggest redownloading entire things from SDL website, because what you have is a mess.

Note that my answer doesn't cover your CMakeLists.txt. What you have is very windows-specific; if you don't care about other systems - be it your way. Your program could be built with CMakeLists you have in your original question thought, that's not a source of your problem.

Whether you want to use static libraries in the first place is questionable. If there is no good reason to specifically use static library i suggest you switch to dynamic ones, ie linking with .dll.a (or just .dll directly, mingw toolchain supports that). That way you don't need your long list of dinput8 dxguid dxerr8 user32 gdi32 winmm imm32 ole32 oleaut32 shell32 version uuid , which are, if you're not aware, dependencies of SDL2 version you use. Meaning if you eg update your static libSDL2.a, this list could be different as it now depends on some more things (that is not a theoretical - latest SDL 2.0.20 actually have longer dependency list).

Explaination of library incompatibility (very long, quite technical, and mostly mingw/gcc specific; if you're not interested in that details, just check final note at the end):

You mentioned undefined reference to 'FUNCTION' in your question, but which FUNCTION is very important; don't omit that information from your future questions, if you ever have that. Thankfully you later provided a link to full libraries archive, and we can see the result of that build attempt ourselves, which is:

../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'

Ok, not a long list actually. So what's undefined? Few SDL_RW* functions and few SDL math functions. Mind you, that's not the only SDL functions SDL2_ttf and SDL2_image use, everything else is found. Why would that functions not be found in your build?.. Linker says there are not functions with that name, well, are there? Since you use mingw toolchain, you should have access for a bit more tools than just a compiler, eg nm tool (in console):

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

(use findstr if you don't have grep ) Yeah, not there. Well, we have SDL2 source, including version histroy, let's see why could that be.

Let's start with eg SDL_RWseek . There is a function with that name in SDL2, which we can view at https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_rwops.h#L401 (just did a search for function name on github and opened a header file). And the comment before that function have an answer: Prior to SDL 2.0.10, this function was a macro. ... This function is available since SDL 2.0.10. . Oh. So whatever you have, seems to be lower than 2.0.10, and SDL2_ttf clearly was build for something newer, where this already was a function.

Ok, what about math functions used by SDL2_image. Well, github search again points to https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_stdinc.h#L572 , which seems to say that it was added at 2.0.2; unfortunately, comment only covers SDL_acos function, with others being uncommented. However, if you switch github view to "blame" (this is not specific to github by the way) to see when each line was last modified, you can see that a lot of those math functions were added with different commits, and across multiple years . What was not found? SDL_fmodf - added 4 years ago, first release is 2.0.8. Same for all other "undefined" functions.

So, those libraries require SDL2 2.0.10 and 2.0.8. What version you have? I don't see a quick way to check to be sure, but there is a SDL_GetVersion function, why don't make minimal program that calls it:

#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;
}

Build&execute (important part is it have to be built within the same environment you have, so, for all intents and purposes, this is a content of your main.c ),

2.0.7

Oh. So yeah, there is no way it can work with that version, there are no functions it wants to call.

In your own answer you said you have built it by adding links to libSDL2_ttf.dll.a and libSDL2_image.dll.a , presumably due to library order. First, library order only matters for dinamic libraries (and even there - not for every linker implementation), so no that's not it. But why did it build? We're going to windows specific territory here, with its concept of "import libraries"; .dll.a is exactly that, a stub that contains symbols with "this will be loaded later at runtime". What it does is makes your resulting program depending on eg SDL2_ttf.dll . Dynamic libraries are very similar to executables and actually have dependency list for themselves (static libraries do not - that's why you have your long list of SDL2 dependencies in your cmakelists), so there is an entry in SDL2_ttf.dll that says it depends on SDL2.dll. Once again, you can check that with your trusty compiler toolchain:

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

What it means is it depends on SDL2.dll , and if i launch it in my current environment, it'll use C:\Windows\system32\SDL2.dll that I have in my default dll search path (your environment could be different). If you check your resulting exe with the same ldd tool, you'll see you now depend on SDL2_ttf.dll , but due to that - to SDL2.dll as well. So now you have statically linked SDL2 in your program, but also pull dynamic SDL2.dll due to dependency. So it builds, maybe even runs, but it is hard to imagine it would be working as intended. I'm pretty sure the moment you'll try to actually do texture or font processing it'll all collapse.

While writing all that I figured out your SDL2.dll is actually 2.0.20, but libSDL2.a is 2.0.7. Maybe best course of action is nuking your entire library archive and redownloading everything from SDL website.

I found the problems:

  1. In sdl2-image-lib and sdl2-ttf-lib directory there are libSDL2_image.dll.a and libSDL2_ttf.dll.a binary files that must be linked.

  2. Order is important in linking. for example libSDL2_image.dll.a must be passed to the linker before libSDL2_image.a

The libraries which I used for my project: https://github.com/satayyeb/sdl2-libraries

and the modified 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_DLL  STATIC IMPORTED)
add_library(SDL2_IMAGE      STATIC IMPORTED)

add_library(SDL2_TTF_DLL    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_DLL  PROPERTY IMPORTED_LOCATION "${SDL2_IMAGE_LIB_DIR}/libSDL2_image.dll.a")
set_property(TARGET SDL2_IMAGE      PROPERTY IMPORTED_LOCATION "${SDL2_IMAGE_LIB_DIR}/libSDL2_image.a")

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


# Order is important! SDL2_IMAGE_DLL must called before SDL2_IMAGE and SDL2_TTF_DLL before SDL2_TTF
set(SDL2_LIBS mingw32 SDL2 SDL2main SDL2_GFX SDL2_TTF_DLL SDL2_TTF SDL2_IMAGE_DLL 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})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM