c++cmakesdl-2static-linkingsdl-image

Linking SDL2/SD2_image statically with CMake


I'm trying to add SDL2 as a library to my project. I want to link it statically. I'm new to c++.

1 - Why does the SDL website recommend linking dynamically whenever possible?

I understand the benefits of dynamic libs. However, assuming users will have all the libraries you need already installed and ready to go in their system is a pretty big assumption IMO. The only case where linking dynamically sounds like a good idea to me is where you are using well know libraries that ship with the OS/platform. https://wiki.libsdl.org/Installation

2 - Linking dynamically seems to automatically find the intrinsic dependencies of (SDL2 and SDL2_image). Linking statically does not. Why is this the case? Here's my FindSDL2_image.cmake file


find_path(SDL2_IMAGE_INCLUDE_DIR SDL_image.h)
include_directories(${SDL2_IMAGE_INCLUDE_DIR})

# PREFER STATIC LIBRARIES ########
# cmake respects the order of extensions when looking for libraries
SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
# ------------------- ########

find_library(SDL2_IMAGE_LIBRARY NAMES SDL2_image PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX})
set(SDL2_IMAGE ${SDL2_IMAGE_LIBRARY})

This links sdl2_image statically. It doesn't link properly because Undefined symbols:

  "_png_set_strip_16", referenced from:
      _IMG_LoadPNG_RW in libSDL2_image.a(IMG_png.o)
  "_png_set_write_fn", referenced from:
      _IMG_SavePNG_RW_libpng in libSDL2_image.a(IMG_png.o)
  "_png_write_png", referenced from:
      _IMG_SavePNG_RW_libpng in libSDL2_image.a(IMG_png.o)

If I remove the section ### PREFER STATIC LIBRARIES ## on the cmake file. It links dynamically and everything works as expected. Why when linking dynamically the intrinsic dependencies are resolved but not when linking statically?

----UPDATE----

I was able to link sdl2_image statically by including its dependencies explicitly

find_library(PNGLIB png)
find_library(JPEG jpeg)
find_library(TIFF tiff)
find_library(WEBP webp)
find_library(LZ z)

target_link_libraries(smb ${SDL2} ${PNGLIB} ${JPEG} ${TIFF} ${WEBP} ${SDL2_IMAGE} ${LZ})

However, this will not scale well for me. Figuring out what these dependencies took a bit of guesswork and googling. Ideally, I'd like CMAKE to pull these in automatically.


Solution

  • it looks like there are several questions, I'll try my best to answer the question step by step:

    Why does the SDL website recommend linking dynamically whenever possible

    One of the reason to link you application dynamically against a library is to decouple the application from the library (it is called shared library / .so in this case). You could update your library without the necessity of recompiling your application code. E.g. in case you have finished your project, and your client have your application running, I suppose, it is not likely that you want to recompile your application code, once there is a bug fix of the underlying library you are using.

    On the other side, by linking your application statically, you're binding your application with that library (.lib or .a form). Means every changes in the library will cause you to recompile your code. Sometime this is wished, e.g. you have provide your client a warranty of your application, usually you want to be sure that no future problem with your library would cause your application to be crashed.

    I have a short example code to understand this better: CMakeLists.txt:

    cmake_minimum_required (VERSION 3.0)
    project(linkageLibrary)
    
    set(STATIC_LIB lib_static)
    set(SHARE_LIB lib_share)
    
    set(STATIC_OTHER_LIB lib_otherstatic)
    set(SHARE_OTHER_LIB lib_othershare)
    
    
    add_library(${STATIC_LIB} STATIC ${STATIC_LIB}.cpp)
    add_library(${SHARE_LIB} SHARED ${SHARE_LIB}.cpp)
    
    # not yet in usage...
    add_library(${STATIC_OTHER_LIB} STATIC ${STATIC_OTHER_LIB}.cpp)
    add_library(${SHARE_OTHER_LIB} SHARED ${SHARE_OTHER_LIB}.cpp)
    
    add_executable(${CMAKE_PROJECT_NAME} main.cpp)
    target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${${CMAKE_PROJECT_NAME}_SOURCE_DIR})
    target_link_libraries(${CMAKE_PROJECT_NAME} ${STATIC_LIB} ${SHARE_LIB})
    
    file(WRITE ${CMAKE_BINARY_DIR}/exchangeShareLibrary.sh "
     echo \"before exchange the static library\"
     ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME} &&
     mv ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so.bk &&
     cp ${CMAKE_BINARY_DIR}/lib${SHARE_OTHER_LIB}.so ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so &&
     echo \"after the shared library has been changed\" &&
     ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}")
    
    file(WRITE ${CMAKE_BINARY_DIR}/exchangeStaticLibrary.sh "
     echo \"before exchange the static library\"
     ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME} &&
     mv ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}.a ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}a.bk &&
     cp ${CMAKE_BINARY_DIR}/lib${STATIC_OTHER_LIB}.a ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}.a &&
     echo \"after the static library has been changed\" &&
     ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}")
    

    main.cpp:

    #include <iostream>
    #include "lib.hpp"
    using namespace std;
    
    int main() {
        printStaticLib();
        printShareLib();
        return 0;
    }
    

    lib.hpp:

    #pragma ONCE
    
    void printStaticLib();
    void printShareLib();
    

    lib_static.cpp:

    #include <iostream>
    #include "lib.hpp"
    using namespace std;
    
    void printStaticLib() {
        cout << "linkage of lib_static" << endl;
    }
    

    lib_share.cpp:

    #include <iostream>
    #include "lib.hpp"
    using namespace std;
    
    void printShareLib() {
        cout << "linkage of lib_share" << endl;
    }
    

    lib_otherstatic.cpp:

    #include <iostream>
    #include "lib.hpp"
    using namespace std;
    
    void printStaticLib() {
        cout << "linkage of the other lib_static with other text" << endl;
    }
    

    lib_othershare.cpp:

    #include <iostream>
    #include "lib.hpp"
    using namespace std;
    
    void printShareLib() {
        cout << "linkage of the other lib_share with other text" << endl;
    }
    

    if you run the generated scripts, you'll notice the printShareLib() will output differently after the .so get exchange, but not the printStaticLib(). (Try to make again without clean-up, if you now execute ./linkageLibrary, you'll get this time also other output for the printStaticLib(). Can you guess why?)

    to your second issue:

    2 - Linking dynamically seems to automatically find the intrinsic dependencies of (SDL2 and SDL2_image). Linking statically does not. Why is this the case?

    without knowing your system setup, I guess the .a static library couldn't be found by your build system. Try to find where it is, and eventually put find_library(SDL2_IMAGE_LIBRARY NAMES SDL2_image HINTS ${_YOUR_SDL2_INSTALLATION_PATH})link

    now back to your question, why SDL2 suggested to be linked dynamically, well it is the decision of the library developer, as you could read in his README

    Please also refer this blog of how using SDL2 with CMake