SDL: Enhanced SDL2Config.cmake to also work on non-Windows

From c3426961721f879f0c326975f6df914681ac3c79 Mon Sep 17 00:00:00 2001
From: Daniel Gibson <[EMAIL REDACTED]>
Date: Sun, 18 Apr 2021 17:15:34 +0200
Subject: [PATCH] Enhanced SDL2Config.cmake to also work on non-Windows

tested Linux; I assume most other Unices are similar; I don't have a Mac
---
 SDL2Config.cmake | 187 +++++++++++++++++++++++++++--------------------
 1 file changed, 109 insertions(+), 78 deletions(-)

diff --git a/SDL2Config.cmake b/SDL2Config.cmake
index fd22ffdf68..abe85e702d 100644
--- a/SDL2Config.cmake
+++ b/SDL2Config.cmake
@@ -1,96 +1,127 @@
 include("${CMAKE_CURRENT_LIST_DIR}/SDL2Targets.cmake")
 
+# provide ${SDL2_LIBRARIES}, ${SDL2_INCLUDE_DIRS} etc, like sdl2-config.cmake does,
+# for compatibility between SDL2 built with autotools and SDL2 built with CMake
 
-# cuts off the path at the last (back)slash
-function(my_dirname FILEPATH RETVAL)
-	string(FIND ${FILEPATH} "/" slashPos REVERSE)
-	string(FIND ${FILEPATH} "\\" bsPos REVERSE) # TODO: untested, cmake always gave me forward slashes
-	if(DEFINED slashPos)
-		if( (DEFINED bsPos) AND (${bsPos} GREATER ${slashPos}) )
-			set(slashPos, ${bsPos})
-		endif()
-	elseif(DEFINED bsPos)
-		set(slashPos, ${bsPos})
-	endif()
-	if(DEFINED slashPos)
-		string(SUBSTRING ${FILEPATH} 0 ${slashPos} dirname)
-		set(${RETVAL} ${dirname} PARENT_SCOPE)
-	endif()
-endfunction()
+# the following seems to work on Windows for both MSVC and MINGW+MSYS and with both SDL2Config/Target.cmake
+# from vcpkg and from building myself with cmake from latest git
+# AND on Linux when building SDL2 (tested current git) with CMake
 
+# the headers are easy - but note that this adds both .../include/ and .../include/SDL2/
+# while the SDL2_INCLUDE_DIRS of sdl2-config.cmake only add ...include/SDL2/
+# But at least if building worked with sdl2-config.cmake it will also work with this.
+get_target_property(SDL2_INCLUDE_DIRS SDL2::SDL2 INTERFACE_INCLUDE_DIRECTORIES)
 
-if(WIN32)
-	# this seems to work for both MSVC and MINGW+MSYS and with both SDL2Config/Target.cmake from vcpkg
-	# and from building myself with cmake from latest git
+# get the paths to the .lib files for both SDL2 and SDL2main
 
-	# ok, the headers are easy - but note that this adds both .../include and .../include/SDL2/
-	# while the SDL2_INCLUDE_DIRS of sdl2-config.cmake only add ...include/SDL2/
-	# But at least if building worked with sdl2-config.cmake it will also work with this.
-	get_target_property(SDL2_INCLUDE_DIRS SDL2::SDL2 INTERFACE_INCLUDE_DIRECTORIES)
+# for the "normal"/release build they could be in lots of different properties..
+set(relprops IMPORTED_IMPLIB_RELEASE IMPORTED_IMPLIB_NOCONFIG IMPORTED_IMPLIB IMPORTED_IMPLIB_MINSIZEREL IMPORTED_IMPLIB_RELWITHDEBINFO
+             IMPORTED_LOCATION_RELEASE IMPORTED_LOCATION_NOCONFIG IMPORTED_LOCATION IMPORTED_LOCATION_MINSIZEREL IMPORTED_LOCATION_RELWITHDEBINFO)
 
-	# get the paths to the .lib files for both SDL2 and SDL2main
-	# they could be in lots of different properties..
-	get_target_property(sdl2implib SDL2::SDL2 IMPORTED_IMPLIB_RELEASE)
-	get_target_property(sdl2implibdbg SDL2::SDL2 IMPORTED_IMPLIB_DEBUG)
+# fewer possibilities for debug builds
+set(dbgprops IMPORTED_IMPLIB_DEBUG IMPORTED_LOCATION_DEBUG)
 
-	# TODO: for all this bla_RELEASE bla_DEBUG etc stuff, maybe only try the ones
-	#       from get_target_property(available_configs SDL2::SDL2 IMPORTED_CONFIGURATIONS) ?
-	#       OR otherwise at least try all of NOCONFIG, RELEASE, MINSIZEREL, RELWITHDEBINFO, DEBUG  (possibly in that order)?
+foreach(prop ${relprops})
+	get_target_property(sdl2implib SDL2::SDL2 ${prop})
+	if(sdl2implib)
+		#message("set sdl2implib from ${prop}")
+		break()
+	else()
+		#message("no luck for sdl2implib with  ${prop}")
+	endif()
+endforeach()
 
-	get_target_property(sdl2mainimplib SDL2::SDL2main IMPORTED_IMPLIB_RELEASE)
-	if(NOT sdl2mainimplib)
-		get_target_property(sdl2mainimplib SDL2::SDL2main IMPORTED_LOCATION_RELEASE)
+foreach(prop ${relprops})
+	get_target_property(sdl2mainimplib SDL2::SDL2main ${prop})
+	if(sdl2mainimplib)
+		#message("set sdl2mainimplib from ${prop}")
+		break()
+	else()
+		#message("no luck for sdl2mainimplib with  ${prop}")
 	endif()
-	get_target_property(sdl2mainimplibdbg SDL2::SDL2main IMPORTED_IMPLIB_DEBUG)
-	if(NOT sdl2mainimplibdbg)
-		get_target_property(sdl2mainimplibdbg SDL2::SDL2main IMPORTED_LOCATION_DEBUG)
+endforeach()
+
+foreach(prop ${dbgprops})
+	get_target_property(sdl2implibdbg SDL2::SDL2 ${prop})
+	if(sdl2implibdbg)
+		#message("set sdl2implibdbg from ${prop}")
+		break()
+	else()
+		#message("no luck for sdl2implibdbg with  ${prop}")
 	endif()
-	
-	if( sdl2implib AND sdl2mainimplib AND sdl2implibdbg AND sdl2mainimplibdbg )
-		# we have both release and debug builds of SDL2 and SDL2main, so use this ugly
-		# generator expression in SDL2_LIBRARIES to support both in MSVC, depending on build type configured there
-		set(SDL2_LIBRARIES $<IF:$<CONFIG:Debug>,${sdl2mainimplibdbg},${sdl2mainimplib}>   $<IF:$<CONFIG:Debug>,${sdl2implibdbg},${sdl2implib}>)
+endforeach()
+
+foreach(prop ${dbgprops})
+	get_target_property(sdl2mainimplibdbg SDL2::SDL2main ${prop})
+	if(sdl2mainimplibdbg)
+		#message("set sdl2mainimplibdbg from ${prop}")
+		break()
 	else()
-		if(NOT sdl2implib) # try to get it from other properties
-			foreach(prop IMPORTED_IMPLIB IMPORTED_IMPLIB_NOCONFIG IMPORTED_IMPLIB_DEBUG IMPORTED_LOCATION_RELEASE IMPORTED_LOCATION_DEBUG)
-				get_target_property(sdl2implib SDL2::SDL2 ${prop})
-				if(sdl2implib)
-					message(STATUS "succeeded with ${prop} => ${sdl2implib}")
-					break()
-				endif()
-				message(STATUS "no luck with ${prop}")
-			endforeach()
-		endif()
-		if(NOT sdl2mainimplib)
-			foreach(prop IMPORTED_IMPLIB IMPORTED_IMPLIB_NOCONFIG IMPORTED_IMPLIB_DEBUG IMPORTED_LOCATION_RELEASE IMPORTED_LOCATION_DEBUG)
-				get_target_property(sdl2mainimplib SDL2::SDL2main ${prop})
-				if(sdl2mainimplib)
-					message(STATUS "succeeded with ${prop} => ${sdl2mainimplib}")
-					break()
-				endif()
-				message(STATUS "no luck with ${prop}")
-			endforeach()
-		endif()
-		
-		if( sdl2implib AND sdl2mainimplib )
-				set(SDL2_LIBRARIES ${sdl2mainimplib}  ${sdl2implib})
-		else()
-				message(FATAL_ERROR, "SDL2::SDL2 and/or SDL2::SDL2main don't seem to contain any kind of IMPORTED_IMPLIB")
-		endif()
+		#message("no luck for sdl2mainimplibdbg with  ${prop}")
 	endif()
+endforeach()
 
-	# NOTE: SDL2_LIBRARIES now looks like "c:/path/to/SDL2main.lib;c:/path/to/SDL2.lib"
-	#       which is different to what it looks like when coming from sdl2-config.cmake
-	#       (there it's more like "-L${SDL2_LIBDIR} -lSDL2main -lSDL2" - and also -lmingw32 and -mwindows)
-	#       This seems to work with both MSVC and MinGW though, while the other only worked with MinGW
+if( sdl2implib AND sdl2mainimplib AND sdl2implibdbg AND sdl2mainimplibdbg )
+	# we have both release and debug builds of SDL2 and SDL2main, so use this ugly
+	# generator expression in SDL2_LIBRARIES to support both in MSVC, depending on build type configured there
+	set(SDL2_LIBRARIES $<IF:$<CONFIG:Debug>,${sdl2mainimplibdbg},${sdl2mainimplib}>   $<IF:$<CONFIG:Debug>,${sdl2implibdbg},${sdl2implib}>)
+else()
+	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()
+
+	if( sdl2implib AND sdl2mainimplib )
+		set(SDL2_LIBRARIES ${sdl2mainimplib}  ${sdl2implib})
+	elseif(WIN32 OR APPLE) # I think these platforms have a non-dummy SDLmain?
+		message(FATAL_ERROR, "SDL2::SDL2 and/or SDL2::SDL2main don't seem to contain any kind of IMPORTED_IMPLIB* or IMPORTED_LOCATION*")
+	elseif(sdl2implib) # on other platforms just libSDL2 will hopefully do?
+		set(SDL2_LIBRARIES ${sdl2implib})
+		message(STATUS, "No SDL2main lib not found, I hope you don't need it..")
+	else()
+		message(FATAL_ERROR, "SDL2::SDL2 doesn't seem to contain any kind of lib to link against in IMPORTED_IMPLIB* or IMPORTED_LOCATION*")
+	endif()
+endif()
 
-	my_dirname(${sdl2implib} SDL2_LIBDIR)
+get_filename_component(SDL2_LIBDIR ${sdl2implib} PATH)
 
-	my_dirname(${SDL2_LIBDIR} SDL2_EXEC_PREFIX) # the exec prefix is one level up from lib/ - TODO: really, always? at least on Linux there's /usr/lib/x86_64-bla-blub/libSDL2-asdf.so.0 ..
-	set(SDL2_PREFIX ${SDL2_PREFIX}) # TODO: could this be somewhere else? parent dir of include or sth?
+# NOTE: SDL2_LIBRARIES now looks like "c:/path/to/SDL2main.lib;c:/path/to/SDL2.lib"
+#       which is different to what it looks like when coming from sdl2-config.cmake
+#       (there it's more like "-L${SDL2_LIBDIR} -lSDL2main -lSDL2" - and also -lmingw32 and -mwindows)
+#       This seems to work with both MSVC and MinGW though, while the other only worked with MinGW
+#       We *could* use if(MSVC) here and make the MinGW case oldschool, BUT keep in mind that
+#       for some reason vcpkg has SDL2.lib and SDL2main.lib in different directories!
+#   On Linux it looks like "/tmp/sdl2inst/lib/libSDL2main.a;/tmp/sdl2inst/lib/libSDL2-2.0.so.0.14.1" which also seems to work
 
-elseif() # not windows
-	# TODO: about the same but don't have to look at *_IMPLIB* as that's windows specific, it should be in IMPORTED_LOCATION*
-	# TODO: make SDL2_LIBRARIES look more like the original (instead of path to libSDL2.so)?
-	# TODO: anything special about macOS? I can't test that, I have no Mac
+if(FALSE) # this (not tested much) could be used to make SDL2_LIBRARIES look/behave more like the original from sdl2-config.cmake
+#if(NOT MSVC) # this is GCC/ld syntax (should also work for mingw and clang), not suitable for MSVC
+
+	if(sdl2mainimplib) # we have libSDL2main
+		# first set libSDL2main and its directory
+		get_filename_component(sdl2main_libdir ${sdl2mainimplib} PATH)
+		set(SDL2_LIBRARIES "-L${sdl2main_libdir} -lSDL2main")
+		# SDL2main can be in a different directory than libSDL2 itself, at least when using vcpkg
+		# if that's the case, add an additional "-L" part for libSDL2's libdir
+		if( NOT (sdl2main_libdir STREQUAL SDL2_LIBDIR) )
+			set(SDL2_LIBRARIES "${SDL2_LIBRARIES} -L${SDL2_LIBDIR}")
+		endif()
+		# lastly, add -lSDL2 itself
+		set(SDL2_LIBRARIES "${SDL2_LIBRARIES} -lSDL2")
+		unset(sdl2main_libdir)
+	else() # no SDL2main, just libSDL2 itself
+		set(SDL2_LIBRARIES "-L${SDL2_LIBDIR} -lSDL2")
+	endif()
 endif()
+
+# the exec prefix is one level up from lib/ - TODO: really, always? at least on Linux there's /usr/lib/x86_64-bla-blub/libSDL2-asdf.so.0 ..
+get_filename_component(SDL2_EXEC_PREFIX ${SDL2_LIBDIR} PATH)
+set(SDL2_PREFIX ${SDL2_EXEC_PREFIX}) # TODO: could this be somewhere else? parent dir of include or sth?
+
+unset(sdl2implib)
+unset(sdl2mainimplib)
+unset(sdl2implibdbg)
+unset(sdl2mainimplibdbg)
+unset(relprops)
+unset(dbgprops)