How is SDL2 supposed to be used with CMake?

First, to avoid misunderstandings: This is not about building SDL2 with CMake. This is about building applications/games that use SDL2 with CMake - with -dev packages from packages managers (of Linux distros, vcpkg, whatever) or the official Development Libraries.

Originally, SDL2 didn’t ship any cmake files, so people used custom FindSDL2.cmake scripts based on the SDL1.2 FindSDL.cmake shipped by CMake itself (example - yes, this is older than SDL 2.0.0!).
Those usually provided ${SDL2_INCLUDE_DIR} (for CMake’s include_directories()) and ${SDL2_LIBRARY} (for CMake’s target_link_libraries()).

SDL 2.0.4 (Jan 2016) introduced sdl2-config.cmake which provide ${SDL2_INCLUDE_DIRS} and ${SDL2_LIBRARIES} (notice the plurals) instead, which I think can be used much in the same way as the ones from the custom FindSDL2.cmake files.

SDL 2.0.6 (Sep 2017) additionally introduced SDL2Config.cmake which appears to work entirely differently. It does not provide ${SDL2_INCLUDE_DIRS} and ${SDL2_INCLUDE_DIRS}.
TBH, it’s hard to understand what it does at all, because SDL2Config.cmake itself does nothing more than include("${CMAKE_CURRENT_LIST_DIR}/SDL2Targets.cmake"), and SDL2Targets.cmake is generated by CMake, does a lot of magic and has no comments that’d describe how to actually use it.
Apparently it defines libraries called SDL2::SDL2 and SDL2::SDL2main that you can then somehow use? Possibly by passing SDL2::SDL2 to CMake’s target_link_libraries() and maybe the include path is set automagically then?
I don’t know and this gets me to the main problem here:

Some Linux distros (or package managers) ship sdl2-config.cmake others ship the incompatible SDL2Config.cmake.
Debian (and thus Ubuntu; I’m on XUbuntu) ships sdl2-config.cmake, Arch Linux ships SDL2Config.cmake, and apparently so does vcpkg (at least that was my impression based on bug reports I got in dhewm3 when I tried removing the custom FindSDL2.cmake).

SDL2-devel-2.0.*-VC.zip contains neither (which is unfortunate, especially with VC it might be handy).
SDL2-devel-2.0.*-mingw.tar.gz contains sdl2-config.cmake, but in a broken state, as it hardcodes /opt/local/x86_64-w64-mingw32. At least that could be relatively easily fixed by using ${CMAKE_CURRENT_LIST_DIR}, which is the current directory sdl2-config.cmake is in, kinda like this.

Anyway, the core problem is that it is (or at least seems to me) very hard to make your own project that uses CMake and SDL build on all the current/relevant Linux distros (and other package managers), because for some reason two incompatible “solutions” exist.
Or am I missing something and it’s indeed feasible to be compatible with either without too much pain?

Wait - if you’re gonna tell me that sdl2-config.cmake now also provides SDL2::SDL2 (like SDL2Config.cmake), that’s nice and all, but not helpful, because that was only added in 2.0.12 (March 2020), and I at least need to support SDL2 back to 2.0.8 on Linux, because Ubuntu 18.04LTS ships that version (and Debian and Raspbian stable ship 2.0.9); even Ubuntu 20.04 LTS ships 2.0.10 (i.e. does not support SDL2::SDL2).

Why are there two different mechanisms anyway?

1 Like

Why are there two different mechanisms anyway?

(Except Windows,) why not “pkg-config sdl2 --libs” and “pkg-config sdl2 --cflags” ? So now three mechanisms. In case using distro-provided packages is hard requirement, I’d guess that would be the best bet; CMake provides FindPkgConfig module and AFAIK it works with any major Linux distros and BSD ports. You should never need SDLmain for pkg-config-aware platforms aside MinGW/MSYS.

When compared with sdl2-config or pkg-config, CMake packaging standard is still young so IMHO i’d recommend to stick with pkg-config or FindSDL2.cmake (that is, find_library)…

pkg-config and sdl2-config don’t work well with CMake:

  • at least sdl2-config has no way to just return the include directory, it’s part of the cflags
  • pkg-config has --cflags-only-I, but that still returns something like -I/usr/include/SDL2, not just the path

and they’re are not available on all platforms:

  • pkg-config is only available on Unix-likes (not even sure if on all of them)
  • sdl2-config is often either not in $PATH on windows, or not available at all - it’s not included in SDL2-devel-2.0.*-VC.zip

A custom FindSDL2.cmake seems to work best, but it’s ugly and only works if SDL2 is installed in one of a few hardcoded places (or if the SDL2DIRS environment variable is explicitly set).

OTOH, the SDL2 CMake configs are also only available if either installed in some standard path CMake searches or if you tell CMake to search there - but the difference is, that these standardpaths are hardcoded into the local CMake installation, while the ones in FindSDL2.cmake are hardcoded into your own file that’s part of your source code…

FindPkgConfig( FindPkgConfig — CMake 3.20.1 Documentation ) will do all required plumbing. Its pkg_check_modules will set SDL2_INCLUDE_DIRS using --cflags-only-I option – it parses pkg-config output automagically. pkg-config is the standard way to manage X11 packages thus I’d be surprised if there were any GUI-enabled distro lacking pkg-config (or pkgconf as drop-in replacement)…

Windows and macOS simply lacks standard library packaging system so you will have to do manual configuration or explicitly target some particular 3rd party packaging like vcpkg or homebrew, regardless whether or not your project is in CMake. sdl2-config.cmake or SDL2Config.cmake is for CMake standard packaging and it may fill this gap in the future – currently, SDL’s CMake buildsystem is still in experimental state IIRC thus official package does not generate proper SDL2Targets.cmake (so it’s hardly usable, and so I’m not sure how devs think about CMake buildsystem…).

So, if you really wanted to avoid any hardcoded paths, I would do: find_package(PkgConfig) first, then if the system had one, use pkg_check_modules to get paths. If the system could not find pkgconfig or path, ask users to set SDL2 development files path manually through some variable and use it.

(I use Vulkan SDK for the purpose on the Windows. Its installer will set environment variable and provides .lib and headers as well as SDL2 DLL. )

1 Like

So it seems that right now the most reliable way to use SDL2 with CMake is (at least on non-Windows) to use FindPkgConfig… that’s good to know but a bit sobering :-/

I thought about what could be done to improve things a bit.
I think it would be very desirable that SDL2Config.cmake also provides SDL2_LIBRARIES and SDL2_INCLUDE_DIRS, ideally also the other things defined in sdl2-config.cmake (SDL2_LIBDIR, SDL2_EXEC_PREFIX, SDL2_PREFIX). That way both sdl2 configs for cmake would behave about the same, at least in the future.

I think this is preferable over expecting everyone to switch to using SDL2::SDL2, because:

  • I assume that there’s lots of existing code using SDL2_LIBRARY_DIRS etc, because most distros ship sdl2-config.cmake (I checked debian/Ubuntu, SUSE, centos, the FreeBSD package)
  • So far the only ones I know of shipping SDL2Config.cmake are Arch (and derivatives) and vcpkg. The good thing about those is that (AFAIK) they always ship the latest release version of SDL2, so the change would reach affected users long before for example Ubuntu LTS users will get an SDL2 version that supports SDL2::SDL2 (from sdl2-config.cmake of SDL 2.0.12+) - assuming SDL 2.0.16 will be released before the next Ubuntu LTS release (22.04) :wink:
    • My guess is that what .cmake file is shipped depends on whether they build SDL2 with autotools (=> sdl2-config.cmake) or CMake (=> SDL2Config.cmake)?

But this is pure theory until we figure out how to modify SDL2Config.cmake to export SDL2_LIBRARY_DIRS etc - and what those should look like. The general idea would be to extract them from the CMake library target SDL2::SDL2. “They” are properties of that target, which can be accessed like get_target_property(<newvarname> <target> <property>).

For the include paths it’s easy enough: get_target_property(SDL2_INCLUDE_DIRS SDL2::SDL2 INTERFACE_INCLUDE_DIRECTORIES).

But SDL2_LIBRARY_DIRS is more difficult:

  1. I’m not even sure what this should even look like for MSVC (Visual C++). For GCC/MinGW and similar, it looks like "-L/sdl2/install/prefix/lib/ -lSDL2". MinGW seems to have some additional linker flags, like -lmingw32 -mwindows. So it’s arguments for a ld-like linker, most probably not useful for MSVC?
    • when I tried to get rid of FindSDL2.cmake in dhewm3, I did something like the following in sdl2-config.cmake in the repo holding the windows build dependencies of dhewm3:
      if(MSVC)
        set(SDL2_LIBRARIES "SDL2")
      else()
        set(SDL2_LIBRARIES "-lmingw32 -lSDL2 -mwindows")
      endif()
      
      This doesn’t contain SDL2main (as dhewm3 uses its own patched SDL_main), but that could be added of course (I think like set(SDL2_LIBRARIES "SDL2;SDL2main")), but it also requires an explicit link_directories(${SDL2_LIBDIR}) which for non-MSVC is not necessary (as the libdir is also set in SDL2_LIBRARIES).
      And keep in mind that for a more generic cmake config (that’s not specific for windows), even more if()s are needed to handle non-Windows platforms - and to make it work with vcpkg’s SDL2 package one would probably also have to handle SDL2 vs SDL2d
    • This is also a problem of sdl2-config.cmake, of course, not just one of making SDL2Config.cmake behave like it.
  2. Related: Should SDL2_LIBRARY_DIRS contain SDL2main or not? SDL2::SDL2 doesn’t seem to automatically link against SDL2main (at least on Windows/vcpkg), there’s an explicit SDL2::SDL2main for that. I’d actually like that (as I mentioned, dhewm3 doesn’t use SDL2main, even on Windows) - but the current behavior of SDL2_LIBRARY_DIRS (if it’s available and works at all) is to link both
  3. It’s unclear which target properties of SDL2::SDL2 hold the information that’s needed.
    • In the SDL2 version of vcpkg, there’s IMPORTED_IMPLIB_DEBUG and IMPORTED_IMPLIB_RELEASE which contain the full paths to SDL2.lib and SDL2d.lib - yes, apparently the vcpkg SDL2 package maintainer thought it was a good idea to provide a debug and a release version of SDL2 (UPDATE: turns out, when creating a SDL2 debug build with cmake on Windows you always get SDL2d.lib/.dll - maybe it’s a CMake thing or even intentional), even though SDL2 (at least by default) doesn’t link against the C runtime and thus should be unaffected by that MS CRT debug vs release runtime mess.
    • On Arch Linux I’ve been told it’s in IMPORTED_LOCATION_NOCONFIG, which is not defined in vcpkg (I’m not sure what exactly it looks like, but it should probably contain the full path to libSDL2.so or something)
    • As this is all from code magically generated by CMake, I could imagine that the property that actually holds that could change between CMake versions? I mean, if you use SDL2::SDL2 like you’re supposed to (just passing it to target_link_libraries() instead of trying to extract information from it), it doesn’t matter which of several roughly equivalent properties holds it…

An alternative to trying to recreate an equivalent SDL2_LIBRARY_DIRS might be just set(SDL2_LIBRARY_DIRS SDL2::SDL2) - even though they are quite different things, both can be passed to target_link_libraries() to link the specified target against SDL2.
If however someone does something more fancy that relies on SDL2_LIBRARY_DIRS being a string (like printing it or maybe trying to cut out -lSDL2main or whatever), that will fail of course.

Well… does anyone have any thoughts on all this? Ideas how to best make it work?

Small Update: I think I’ll be able to get SDL2Config.cmake to provide SDL2_LIBRARY_DIRS, SDL2_INCLUDE_DIRS etc - I have something that works on Windows for both MSVC and MinGW, and for both self-compiled (with CMake+MSVC) SDL2 and the one provided by vcpkg; Linux etc still TODO - but gotta sleep before I can show anything.

I posted a (not so short) summary and some additional thoughts at [cmake] SDL2Config and SDL2Target not exporting SDL2_INCLUDE_DIRS · Issue #4004 · libsdl-org/SDL · GitHub

A (WIP) Pull Request addressing some of these issues: WIP: Improve sdl2-config.cmake.in and SDL2Config.cmake by DanielGibson · Pull Request #4320 · libsdl-org/SDL · GitHub

Hello,

I found this thread searching through google, and it seems like I’m not alone. I’m not a developer, but I am trying to build someone else’s project who does use C99 and the latest version of SDL2 and I’m stumped.

I’m currently using Mingw and Cmake, and I can’t figure out how to get this project to build properly.

This is what part of the CMAKE looks like. I tried pointing it towards the SDL2 source code, and CMAKE asks me for SDL2targets which doesn’t even make sense because it gets created when you build SDL2 separately. I really don’t know how this person figured out how to build with it when it seems people in this thread are having problems too. Can anyone chime in? Sorry for the dumb questions. Thanks.

find_package(SDL2 REQUIRED)
find_package(PhysFS REQUIRED)
set(PLATFORM_LIBRARIES
	SDL2::SDL2
	${PHYSFS_LIBRARY}
)
set(PLATFORM_INCLUDE_DIRS
	${CMAKE_CURRENT_BINARY_DIR}
	${SRC}
	${SRC}/Platform/${PLATFORM_NAME}/PlatformSupport
	${SDL2_INCLUDE_DIRS}
	${PHYSFS_INCLUDE_DIR}
)

Hi @meijihuaren!
Did you (try to) build SDL2 yourself?
If you want to build it with MinGW and want it to work with CMake at all, I’d suggest building it with autotools (./configure --prefix=/path/to/install/sdl2/ then make -j8 and finally make install), as the SDL2Config.cmake (or rather SDL2Targets.cmake) generated by CMake for MinGW is completely broken at the moment.
Afterwards you should be able to set SDL2_DIR to the directory sdl2-config.cmake was installed to (should be something like /path/to/install/sdl2/lib/cmake/SDL2/) and in CMakeLists.txt do

find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIR})
target_link_libraries(YourApp ${SDL2_LIBRARIES})

Hello, thanks for responding.

Yes I tried to build SDL2 myself, which is how I found out about the SDL2targets.cmake being made there. I had no idea I had to build SDL2 to use it with the library instead of just pointing the directory to the build files of SDL2.

I tried using MSYS to use autotools, and .configure worked fine. However, when I ran make -j8, I got a bunch of errors such as:

src/joystick/windows/SDL_rawinputjoystick.c:282:5: error: unknown type name 'XINPUT_STATE_EX'
     XINPUT_STATE_EX state;
     ^~~~~~~~~~~~~~~
src/joystick/windows/SDL_rawinputjoystick.c:283:5: error: unknown type name 'XINPUT_BATTERY_INFORMATION_EX'
     XINPUT_BATTERY_INFORMATION_EX battery;
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/joystick/windows/SDL_rawinputjoystick.c:287:16: error: 'XUSER_MAX_COUNT' undeclared here (not in a function)
 } xinput_state[XUSER_MAX_COUNT];
                ^~~~~~~~~~~~~~~
src/joystick/windows/SDL_rawinputjoystick.c: In function 'RAWINPUT_UpdateXInput':
src/joystick/windows/SDL_rawinputjoystick.c:297:13: error: unknown type name 'XINPUT_CAPABILITIES'
             XINPUT_CAPABILITIES capabilities;
             ^~~~~~~~~~~~~~~~~~~

I have no clue what’s going on. I don’t know autotools at all so I’m not sure if this is normal or not.

Sounds like some directx headers are incomplete - maybe you have a very old version of mingw?

BTW, If you’re on Windows, I’d suggest using Visual Studio (the free Community Edition), it tends to be less painful and has a better debugger.

I know this is an uncommon scenario (although it’s extremely important to me) but perhaps worth noting that Microsoft’s compiler provides no support at all for 80-bit (extended precision) long doubles, so if you need this data type you must use GCC.

My Pull Request that hopefully fixes some of these issues is now ready: Improve sdl2-config.cmake.in and SDL2Config.cmake by DanielGibson · Pull Request #4320 · libsdl-org/SDL · GitHub - it would be cool of someone could test it, especially if you’re not using Windows or Linux (those are the platforms I could test myself).

There is a small test application at SDL2 + CMake test · GitHub
Build and install SDL2 yourself (with the branch of the pull request that you can also download as a .zip) - either with CMake or with autotools (./configure && make -j4 && make install) or ideally try both :slight_smile:
Then try to build my test application with that freshly installed SDL2:
In the directory with my CMakeLists.txt and main.cpp, do

mkdir build
cd build
cmake -DSDL2_DIR=/path/to/sdl2-installation/lib/cmake/SDL2/ ..
make

This should create two executables, SDL2Test and SDL2Test2 that should both show a grey window when started - it can be closed with [Q] or Esc.

Thanks in advance!

Hello, I recently downloaded and built the source for SDL2 with CMake (Unix Makefile) on MacOS.

The changes you recently made this year have been merged with the latest version (2.0.16) as far as I know, but I am facing an issue trying to get SDL2 to work with CMake and CLion (JetBrains C++ IDE).

I posted this question in the (unofficial?) Discord, but figured I could create a post here as well since it seemed relevant.

I built SDL2 from source and have the files in this directory:

/Users/usernamehere/SDL2/SDL2-2.0.16-install

Here is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)

project(HelloSDL2)

set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES main.cpp)

add_executable(HelloSDL2 ${SOURCE_FILES})

include_directories(/Users/usernamehere/SDL2/SDL2-2.0.16-install)
set(SDL2_DIR /Users/usernamehere/SDL2/SDL2-2.0.16-install/lib/cmake/SDL2)

find_package(SDL2 REQUIRED)

INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${SDL2_LIBRARY})

Unfortunately, I am getting the following error:

CMake Error at /Users/usernamehere/SDL2/SDL2-2.0.16-install/lib/cmake/SDL2/SDL2Config.cmake:94 (get_filename_component):
  get_filename_component called with incorrect number of arguments

Where the method:
get_filename_component

Is called in SDL2Config.cmake:
get_filename_component(SDL2_EXEC_PREFIX ${SDL2_LIBDIR} PATH)

I’m assuming I’ve missed some other configuration I need to add to my CMakeLists.txt, or maybe its due to where I installed SDL2 since it isn’t in the default usr/local directory.

Maybe you (or someone who finds this topic) can help me with this issue.

Update
Ok, in my particular case, I did a Debug build for SDL2. So this problem might not have occurred for others that did a Normal/Release build.

In the SDL2Config.cmake file, there is a condition that tries to replace the two variables:

sdl2implib
sdl2mainimplib

with the debug variables:

sdl2implibdbg
sdl2mainimplibdbg

However, the code was not extracting the actual path from the Debug variables using the ${…} notation.

Here is the change that fixed it for me.

if( (NOT sdl2implib) AND sdl2implibdbg ) # if we only have a debug version of the lib
    set(sdl2implib ${sdl2implibdbg})
endif()
if( (NOT sdl2mainimplib) AND sdl2mainimplibdbg ) # if we only have a debug version of the lib
    set(sdl2mainimplib ${sdl2mainimplibdbg})
endif()
1 Like

I simply use the FetchContent on its source code and build it linked to my project and it works fine. :slight_smile:

Damn, you’re right of course, I missed that.
I blame CMakes inconsistent syntax :stuck_out_tongue:

There’s a fix in https://github.com/libsdl-org/SDL/pull/4814 (already merged)