Maelstrom: Added a cmake option USE_VENDORED_SDL, which defaults to ON

From 1186beba81e0826e05966ae28aae12a72f290613 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 26 Apr 2026 22:56:00 -0700
Subject: [PATCH] Added a cmake option USE_VENDORED_SDL, which defaults to ON

This allows Linux distributions to use the system version of SDL
---
 CMakeLists.txt          |  26 +++++--
 cmake/sdlcpu.cmake      | 158 ++++++++++++++++++++++++++++++++++++++++
 cmake/sdlplatform.cmake |  77 ++++++++++++++++++++
 3 files changed, 256 insertions(+), 5 deletions(-)
 create mode 100644 cmake/sdlcpu.cmake
 create mode 100644 cmake/sdlplatform.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d7ce918b..0a5a863b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.0...4.0)
 set(MAJOR_VERSION 4)
 set(MINOR_VERSION 0)
 set(MICRO_VERSION 0)
+set(SDL_REQUIRED_VERSION 3.4.4)
 
 project(Maelstrom
     LANGUAGES C CXX
@@ -25,13 +26,21 @@ if(EMSCRIPTEN)
     set(SDL_EMSCRIPTEN_PERSISTENT_PATH /storage)
 endif()
 
-add_subdirectory(maclib)
-
 set(PHYSFS_BUILD_SHARED OFF)
 set(PHYSFS_BUILD_TEST OFF)
 add_subdirectory(external/physfs EXCLUDE_FROM_ALL)
 
-add_subdirectory(external/SDL EXCLUDE_FROM_ALL)
+option(USE_VENDORED_SDL "Use the vendored version of SDL" TRUE)
+if (USE_VENDORED_SDL)
+    add_subdirectory(external/SDL EXCLUDE_FROM_ALL)
+else()
+    find_package(SDL3 ${SDL_REQUIRED_VERSION} REQUIRED COMPONENTS Headers SDL3-shared)
+    macro(dep_option _NAME _DESC _DEFLT _DEPTEST _FAILDFLT)
+      cmake_dependent_option("${_NAME}" "${_DESC}" "${_DEFLT}" "${_DEPTEST}" "${_FAILDFLT}")
+    endmacro()
+    include("cmake/sdlcpu.cmake")
+    include("cmake/sdlplatform.cmake")
+endif()
 
 # SDL_net's ABI has not yet been finalized
 set(original_BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS}")
@@ -39,6 +48,8 @@ set(BUILD_SHARED_LIBS OFF)
 add_subdirectory(external/SDL_net SDL_net EXCLUDE_FROM_ALL)
 set(BUILD_SHARED_LIBS "${original_BUILD_SHARED_LIBS}")
 
+add_subdirectory(maclib)
+
 SDL_DetectTargetCPUArchitectures(SDL_CPU_NAMES)
 SDL_DetectCMakePlatform()
 
@@ -242,7 +253,10 @@ else()
         if(APPLE)
             set_property(TARGET ${TARGET_NAME} SDL3-shared PROPERTY INSTALL_RPATH "@executable_path")
         else()
-            set_property(TARGET ${TARGET_NAME} SDL3-shared PROPERTY INSTALL_RPATH "$ORIGIN")
+            set_property(TARGET ${TARGET_NAME} PROPERTY INSTALL_RPATH "$ORIGIN")
+            if(USE_VENDORED_SDL)
+                set_property(TARGET SDL3-shared PROPERTY INSTALL_RPATH "$ORIGIN")
+            endif(  )
         endif()
     else()
         include(GNUInstallDirs)
@@ -255,7 +269,9 @@ else()
         )
     endif()
 
-    install(TARGETS ${TARGET_NAME} SDL3-shared LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" NAMELINK_SKIP RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+    if (USE_VENDORED_SDL)
+        install(TARGETS ${TARGET_NAME} SDL3-shared LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" NAMELINK_SKIP RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+    endif()
     if(STEAM)
         install(IMPORTED_RUNTIME_ARTIFACTS SteamworksSDK::steam_api)
     endif()
diff --git a/cmake/sdlcpu.cmake b/cmake/sdlcpu.cmake
new file mode 100644
index 00000000..a27e7329
--- /dev/null
+++ b/cmake/sdlcpu.cmake
@@ -0,0 +1,158 @@
+function(SDL_DetectTargetCPUArchitectures DETECTED_ARCHS)
+
+  set(known_archs EMSCRIPTEN ARM32 ARM64 ARM64EC LOONGARCH64 POWERPC32 POWERPC64 RISCV32 RISCV64 X86 X64)
+
+  if(APPLE AND CMAKE_OSX_ARCHITECTURES)
+    foreach(known_arch IN LISTS known_archs)
+      set(SDL_CPU_${known_arch} "0" PARENT_SCOPE)
+    endforeach()
+    set(detected_archs)
+    foreach(osx_arch IN LISTS CMAKE_OSX_ARCHITECTURES)
+      if(osx_arch STREQUAL "x86_64")
+        set(SDL_CPU_X64 "1" PARENT_SCOPE)
+        list(APPEND detected_archs "X64")
+      elseif(osx_arch STREQUAL "arm64")
+        set(SDL_CPU_ARM64 "1" PARENT_SCOPE)
+        list(APPEND detected_archs "ARM64")
+      endif()
+    endforeach()
+    set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE)
+    return()
+  endif()
+
+  set(detected_archs)
+  foreach(known_arch IN LISTS known_archs)
+    if(SDL_CPU_${known_arch})
+      list(APPEND detected_archs "${known_arch}")
+    endif()
+  endforeach()
+
+  if(detected_archs)
+    set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE)
+    return()
+  endif()
+
+  set(arch_check_ARM32 "defined(__arm__) || defined(_M_ARM)")
+  set(arch_check_ARM64 "defined(__aarch64__) || defined(_M_ARM64)")
+  set(arch_check_ARM64EC "defined(_M_ARM64EC)")
+  set(arch_check_EMSCRIPTEN "defined(__EMSCRIPTEN__)")
+  set(arch_check_LOONGARCH64 "defined(__loongarch64)")
+  set(arch_check_POWERPC32 "(defined(__PPC__) || defined(__powerpc__)) && !defined(__powerpc64__)")
+  set(arch_check_POWERPC64 "defined(__PPC64__) || defined(__powerpc64__)")
+  set(arch_check_RISCV32 "defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 32")
+  set(arch_check_RISCV64 "defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64")
+  set(arch_check_X86 "defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)")
+  set(arch_check_X64 "(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)) && !defined(_M_ARM64EC)")
+
+  set(src_vars "")
+  set(src_main "")
+  foreach(known_arch IN LISTS known_archs)
+    set(detected_${known_arch} "0")
+
+    string(APPEND src_vars "
+#if ${arch_check_${known_arch}}
+#define ARCH_${known_arch} \"1\"
+#else
+#define ARCH_${known_arch} \"0\"
+#endif
+const char *arch_${known_arch} = \"INFO<${known_arch}=\" ARCH_${known_arch} \">\";
+")
+    string(APPEND src_main "
+  result += arch_${known_arch}[argc];")
+  endforeach()
+
+  set(src_arch_detect "${src_vars}
+int main(int argc, char *argv[]) {
+  int result = 0;
+  (void)argv;
+${src_main}
+  return result;
+}")
+
+  if(CMAKE_C_COMPILER)
+    set(ext ".c")
+  elseif(CMAKE_CXX_COMPILER)
+    set(ext ".cpp")
+  else()
+    enable_language(C)
+    set(ext ".c")
+  endif()
+  set(path_src_arch_detect "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/SDL_detect_arch${ext}")
+  file(WRITE "${path_src_arch_detect}" "${src_arch_detect}")
+  set(path_dir_arch_detect "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/SDL_detect_arch")
+  set(path_bin_arch_detect "${path_dir_arch_detect}/bin")
+
+  set(detected_archs)
+
+  set(msg "Detecting Target CPU Architecture")
+  message(STATUS "${msg}")
+
+  include(CMakePushCheckState)
+
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
+
+  cmake_push_check_state(RESET)
+  try_compile(SDL_CPU_CHECK_ALL
+    "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/SDL_detect_arch"
+    SOURCES "${path_src_arch_detect}"
+    COPY_FILE "${path_bin_arch_detect}"
+  )
+  cmake_pop_check_state()
+  if(NOT SDL_CPU_CHECK_ALL)
+    message(STATUS "${msg} - <ERROR>")
+    message(WARNING "Failed to compile source detecting the target CPU architecture")
+  else()
+    set(re "INFO<([A-Z0-9]+)=([01])>")
+    file(STRINGS "${path_bin_arch_detect}" infos REGEX "${re}")
+
+    foreach(info_arch_01 IN LISTS infos)
+      string(REGEX MATCH "${re}" A "${info_arch_01}")
+      if(NOT "${CMAKE_MATCH_1}" IN_LIST known_archs)
+        message(WARNING "Unknown architecture: \"${CMAKE_MATCH_1}\"")
+        continue()
+      endif()
+      set(arch "${CMAKE_MATCH_1}")
+      set(arch_01 "${CMAKE_MATCH_2}")
+      set(detected_${arch} "${arch_01}")
+    endforeach()
+
+    foreach(known_arch IN LISTS known_archs)
+      if(detected_${known_arch})
+        list(APPEND detected_archs ${known_arch})
+      endif()
+    endforeach()
+  endif()
+
+  if(detected_archs)
+    foreach(known_arch IN LISTS known_archs)
+      set("SDL_CPU_${known_arch}" "${detected_${known_arch}}" CACHE BOOL "Detected architecture ${known_arch}")
+    endforeach()
+    message(STATUS "${msg} - ${detected_archs}")
+  else()
+    include(CheckCSourceCompiles)
+    cmake_push_check_state(RESET)
+    foreach(known_arch IN LISTS known_archs)
+      if(NOT detected_archs)
+        set(cache_variable "SDL_CPU_${known_arch}")
+          set(test_src "
+        int main(int argc, char *argv[]) {
+        #if ${arch_check_${known_arch}}
+          return 0;
+        #else
+          choke
+        #endif
+        }
+        ")
+        check_c_source_compiles("${test_src}" "${cache_variable}")
+        if(${cache_variable})
+          set(SDL_CPU_${known_arch} "1" CACHE BOOL "Detected architecture ${known_arch}")
+          set(detected_archs ${known_arch})
+        else()
+          set(SDL_CPU_${known_arch} "0" CACHE BOOL "Detected architecture ${known_arch}")
+        endif()
+      endif()
+    endforeach()
+    cmake_pop_check_state()
+  endif()
+  set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/sdlplatform.cmake b/cmake/sdlplatform.cmake
new file mode 100644
index 00000000..6c60e0a2
--- /dev/null
+++ b/cmake/sdlplatform.cmake
@@ -0,0 +1,77 @@
+function(SDL_DetectCMakePlatform)
+  set(sdl_cmake_platform )
+  if(WIN32)
+    set(sdl_cmake_platform Windows)
+  elseif(PSP)
+    set(sdl_cmake_platform psp)
+  elseif(APPLE)
+    if(CMAKE_SYSTEM_NAME MATCHES ".*(Darwin|MacOS).*")
+      set(sdl_cmake_platform macOS)
+    elseif(CMAKE_SYSTEM_NAME MATCHES ".*tvOS.*")
+      set(sdl_cmake_platform tvOS)
+    elseif(CMAKE_SYSTEM_NAME MATCHES ".*iOS.*")
+      set(sdl_cmake_platform iOS)
+    elseif(CMAKE_SYSTEM_NAME MATCHES ".*watchOS.*")
+      set(sdl_cmake_platform watchOS)
+    elseif (CMAKE_SYSTEM_NAME MATCHES "visionOS")
+      set(sdl_cmake_platform visionOS)
+    else()
+      message(WARNING "Unknown Apple platform: \"${CMAKE_SYSTEM_NAME}\"")
+    endif()
+  elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku.*")
+    set(sdl_cmake_platform Haiku)
+  elseif(NINTENDO_3DS)
+    set(sdl_cmake_platform n3ds)
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "DOS")
+    set(sdl_cmake_platform dos)
+  elseif(NGAGESDK)
+    set(sdl_cmake_platform ngage)
+  elseif(PS2)
+    set(sdl_cmake_platform ps2)
+  elseif(RISCOS)
+    set(sdl_cmake_platform RISCOS)
+  elseif(VITA)
+    set(sdl_cmake_platform Vita)
+  elseif(CMAKE_SYSTEM_NAME MATCHES ".*Linux")
+    set(sdl_cmake_platform Linux)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "kFreeBSD.*")
+    set(sdl_cmake_platform FreeBSD)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "kNetBSD.*|NetBSD.*")
+    set(sdl_cmake_platform NetBSD)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "kOpenBSD.*|OpenBSD.*")
+    set(sdl_cmake_platform OpenBSD)
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "GNU")
+    # GNU/Hurd must be checked AFTER RISCOS
+    set(sdl_cmake_platform Hurd)
+  elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
+    set(sdl_cmake_platform BSDi)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "DragonFly.*|FreeBSD")
+    set(sdl_cmake_platform FreeBSD)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "SYSV5.*")
+    set(sdl_cmake_platform SYSV5)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "Solaris.*|SunOS.*")
+    set(sdl_cmake_platform Solaris)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "HP-UX.*")
+    set(sdl_cmake_platform HPUX)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "AIX.*")
+    set(sdl_cmake_platform AIX)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "Minix.*")
+    set(sdl_cmake_platform Minix)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "Android.*")
+    set(sdl_cmake_platform Android)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "Emscripten.*")
+    set(sdl_cmake_platform Emscripten)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "QNX.*")
+    set(sdl_cmake_platform QNX)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "BeOS.*")
+    message(FATAL_ERROR "BeOS support has been removed as of SDL 2.0.2.")
+  endif()
+
+  if(sdl_cmake_platform)
+    string(TOUPPER "${sdl_cmake_platform}" _upper_platform)
+    set("${_upper_platform}" TRUE PARENT_SCOPE)
+  else()
+    set(sdl_cmake_platform "unknown")
+  endif()
+  set(SDL_CMAKE_PLATFORM "${sdl_cmake_platform}" PARENT_SCOPE)
+endfunction()