My goal is to build a C++ app with SDL2 that can run on both Linux and Windows which may not have SDL2 installed. (I know that there are lot of posts about it, I'll come later).
So I'm going through my little project structure and I remember using CMake to make building more efficient. So I document myself a lot and conclude to this structure:
- app (contains the main function)
|- main.cpp
|- CMakeLists.txt
- build
- libs (contains externals libraries such as: "spdlog", "googletest")
- src
|- all the source files
|- CMakeLists.txt
- tests
|- tests files
|- CMakeLists.txt
CMakeLists.txt
Structure is not the point of this discussion.
Now that my project structure is done, I focus on my build files.
Thanks to a lot of posts, I understand that I should go through static linking. (I know the cons such as recompiling statics libraries, not having last updates automatically, larger files, ...). I want to distribute my program as a "folder" so I excluded the solution of (package) installers. My program must be able to run with his own resources (folders). Also, remember that SDL2 came with the "zlib license" allowing static link.
So I write my src/CMakeLists.txt:
set(PROJ_LIB_NAME "projectlib")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/config/cmake_config.hpp.in
${CMAKE_CURRENT_SOURCE_DIR}/config/cmake_config.hpp
)
set(PROJ_LIB_SOURCES
core.cpp
utils/logs_utils.cpp
gui/gui.cpp
)
add_library(${PROJ_LIB_NAME} STATIC ${PROJ_LIB_SOURCES})
target_include_directories(${PROJ_LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
### ====================================
### LIBRARIES
### ====================================
### spdlog
### ====================================
if(NOT TARGET spdlog)
# Stand-alone build
find_package(spdlog REQUIRED)
endif()
target_link_libraries(${PROJ_LIB_NAME} PRIVATE spdlog::spdlog)
### SDL2
### ====================================
find_package(SDL2 REQUIRED)
target_include_directories(${PROJ_LIB_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(${PROJ_LIB_NAME} PRIVATE -static ${SDL2_LIBRARIES})
Please note the -static
at the last line
And my CMakeLists.txt (root):
cmake_minimum_required(VERSION 3.16.3 FATAL_ERROR)
enable_testing()
set(PROJ_NAME "progr")
set(PROJ_VERSION "1.0.0")
# set the project name
project(${PROJ_NAME} VERSION ${PROJ_VERSION})
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# specify compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wpedantic")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
add_subdirectory(${CMAKE_SOURCE_DIR}/libs/googletest-1.11.0)
add_subdirectory(${CMAKE_SOURCE_DIR}/libs/spdlog-1.8.5)
add_subdirectory(${CMAKE_SOURCE_DIR}/src)
# add_subdirectory(${CMAKE_SOURCE_DIR}/tests)
# add_subdirectory(${CMAKE_SOURCE_DIR}/app)
Finally, the app/CMakeLists.txt:
set(PROJ_EXE_NAME "main")
add_executable(${PROJ_EXE_NAME} main.cpp)
target_link_libraries(${PROJ_EXE_NAME} PUBLIC projectlib)
Seems good but doesn't work. If I uncomment add_subdirectory(${CMAKE_SOURCE_DIR}/app)
to build my app, I get a bunch of undefined references coming from "/usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.a", " ../libs/spdlog-1.8.5/libspdlogd.a", "/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libSDL2.a"...
What I've tried so far to make my app standalone portable:
- downloading the SDL2 source code and put it in libs/, telling CMake to treat it like a subdirectory ->
that was dumb because the source code is aiming to install SDL on your computer, not to be used as it by your project...(following SDL installation guide, you can) - looking for a findSDL2.cmake file -> do I really need of those incomprehensible file? Also, it was good for SDL1 but not anymore for SDL2...
- playing with so much differents parameters in CMake since 2 days...
- many more that I can't remember for now...
There is not a single solution that everyone applauds.
I don't understand why there are no tutorials on how to achieve the goal of making a C++ SDL2 portable app even on machines where SDL2 is not installed... Everyone seems to agree that apps like Steam will definitely be installed and that it is up to them to manage SDL. What about games that don't go through Steam then? But then again I wonder if this is a good idea. As a JS developer, when I build an app with NPM dependencies, I block my packages versions so that all developers are working with the same tools and my production environment is stable. I don't understand this dynamic library logic so I'll be very happy to have explanations :)
EDIT: Forgot to mention that my project library (src/CMakeLists.txt) is building well and all goes wrong when I try to link it with my main fucntion (app/CMakeLists.txt)
EDIT 2: Have a working static link but with some others dependencies that must be downloaded... What I've done so far:
- downloaded the SDL source code and put it in my /libs folder (https://www.libsdl.org/download-2.0.php) (No need to create a build directory inside and run 'make' or 'configure' commands. CMakeLists will handle this for you. Also, do not 'make install')
- modify my root CMakeLists.txt to add
add_subdirectory(${CMAKE_SOURCE_DIR}/libs/SDL2-2.0.14)
before building my sources - modify my src/CMakeLists.txt to comment all the SDL find_package things (etc...) and add only the libraries linking. See:
### SDL2
### ====================================
## Comments
#set(SDL2_DIR ${CMAKE_SOURCE_DIR}/libs/SDL2-2.0.14)
#find_package(SDL2 REQUIRED)
#target_include_directories(${PROJ_LIB_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
#target_link_libraries(${PROJ_LIB_NAME} PRIVATE -static ${SDL2_LIBRARIES})
## end Comments
## the only needed link to SDL2
target_link_libraries(${PROJ_LIB_NAME} PRIVATE SDL2main SDL2-static)
- after that, running my app give me some errors as "No available video device" at the initialization of SDL but my app was building!
- turns out that I need some more dependencies which are installed (I guess) when you do
sudo apt install libsdl2-dev
(what we want to avoid). You can found all the dependencies here: https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md#build-dependencies - I've installed the dependencies and... Everything works fine!
I guess my new problem now is "What if I try to run my app on a computer that does not have these dependencies?". Asking that because I manage to compile and run my app before installing them. I conclude that my executable has the SDL code in it but not the dependencies code.
I still wonder why the static link is always so demonizing when for me it allows to have control over the versions of the dependencies. I agree with the "bug fixes" argument, but these new versions may be incompatible with your app. In this case, you should explain to your users that you are working very hard to release a fix when your code is not causing the regression...